diff options
30 files changed, 2958 insertions, 911 deletions
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 8ee0fa91e4a0..c7b29df4a963 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -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>; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt index 2b2caa281ce3..255ece3043ad 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt @@ -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, diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt index b7f3a9325ebd..6e86d8aa29b4 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt @@ -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. diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt index e64dbdea7db9..11046429a118 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt @@ -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. diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt new file mode 100644 index 000000000000..e24436fc5ea9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5514.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt index efc48c65198d..e41085818559 100644 --- a/Documentation/devicetree/bindings/sound/rt5616.txt +++ b/Documentation/devicetree/bindings/sound/rt5616.txt @@ -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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e2f68807d970..4b89c95a5dec 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1692,100 +1692,63 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, EXPORT_SYMBOL_GPL(regmap_raw_write); /** - * regmap_field_write(): Write a value to a single register field - * - * @field: Register field to write to - * @val: Value to be written - * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. - */ -int regmap_field_write(struct regmap_field *field, unsigned int val) -{ - return regmap_update_bits(field->regmap, field->reg, - field->mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_field_write); - -/** - * regmap_field_update_bits(): Perform a read/modify/write cycle - * on the register field + * regmap_field_update_bits_base(): + * Perform a read/modify/write cycle on the register field + * with change, async, force option * * @field: Register field to write to * @mask: Bitmask to change * @val: Value to be written + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update * * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ -int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val) +int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) { mask = (mask << field->shift) & field->mask; - return regmap_update_bits(field->regmap, field->reg, - mask, val << field->shift); + return regmap_update_bits_base(field->regmap, field->reg, + mask, val << field->shift, + change, async, force); } -EXPORT_SYMBOL_GPL(regmap_field_update_bits); +EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); /** - * regmap_fields_write(): Write a value to a single register field with port ID - * - * @field: Register field to write to - * @id: port ID - * @val: Value to be written - * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. - */ -int regmap_fields_write(struct regmap_field *field, unsigned int id, - unsigned int val) -{ - if (id >= field->id_size) - return -EINVAL; - - return regmap_update_bits(field->regmap, - field->reg + (field->id_offset * id), - field->mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_fields_write); - -int regmap_fields_force_write(struct regmap_field *field, unsigned int id, - unsigned int val) -{ - if (id >= field->id_size) - return -EINVAL; - - return regmap_write_bits(field->regmap, - field->reg + (field->id_offset * id), - field->mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_fields_force_write); - -/** - * regmap_fields_update_bits(): Perform a read/modify/write cycle - * on the register field + * regmap_fields_update_bits_base(): + * Perform a read/modify/write cycle on the register field + * with change, async, force option * * @field: Register field to write to * @id: port ID * @mask: Bitmask to change * @val: Value to be written + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update * * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ -int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, - unsigned int mask, unsigned int val) +int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) { if (id >= field->id_size) return -EINVAL; mask = (mask << field->shift) & field->mask; - return regmap_update_bits(field->regmap, - field->reg + (field->id_offset * id), - mask, val << field->shift); + return regmap_update_bits_base(field->regmap, + field->reg + (field->id_offset * id), + mask, val << field->shift, + change, async, force); } -EXPORT_SYMBOL_GPL(regmap_fields_update_bits); +EXPORT_SYMBOL_GPL(regmap_fields_update_bits_base); /* * regmap_bulk_write(): Write multiple registers to the device @@ -2653,76 +2616,36 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, } /** - * regmap_update_bits: Perform a read/modify/write cycle on the register map - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * - * 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 ret; - - map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, NULL, false); - map->unlock(map->lock_arg); - - return ret; -} -EXPORT_SYMBOL_GPL(regmap_update_bits); - -/** - * regmap_write_bits: Perform a read/modify/write cycle on the register map - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * - * Returns zero for success, a negative number on error. - */ -int regmap_write_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val) -{ - int ret; - - map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, NULL, true); - map->unlock(map->lock_arg); - - return ret; -} -EXPORT_SYMBOL_GPL(regmap_write_bits); - -/** - * regmap_update_bits_async: Perform a read/modify/write cycle on the register - * map asynchronously + * 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_async(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); - map->async = true; + map->async = async; - ret = _regmap_update_bits(map, reg, mask, val, NULL, false); + ret = _regmap_update_bits(map, reg, mask, val, change, force); map->async = false; @@ -2730,69 +2653,30 @@ int regmap_update_bits_async(struct regmap *map, unsigned int reg, 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); +EXPORT_SYMBOL_GPL(regmap_update_bits_base); /** - * regmap_update_bits_check_async: Perform a read/modify/write cycle on the - * register map asynchronously and report if - * updated + * regmap_write_bits: Perform a read/modify/write cycle on the register map * * @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 regmap_write_bits(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, change, false); - - map->async = false; - + ret = _regmap_update_bits(map, reg, mask, val, NULL, true); map->unlock(map->lock_arg); return ret; } -EXPORT_SYMBOL_GPL(regmap_update_bits_check_async); +EXPORT_SYMBOL_GPL(regmap_write_bits); void regmap_async_complete_cb(struct regmap_async *async, int ret) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 18394343f489..e0960b3ff290 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -65,6 +65,33 @@ struct reg_sequence { unsigned int delay_us; }; +#define regmap_update_bits(map, reg, mask, val) \ + regmap_update_bits_base(map, reg, mask, val, NULL, false, false) +#define regmap_update_bits_async(map, reg, mask, val)\ + regmap_update_bits_base(map, reg, mask, val, NULL, true, false) +#define regmap_update_bits_check(map, reg, mask, val, change)\ + regmap_update_bits_base(map, reg, mask, val, change, false, false) +#define regmap_update_bits_check_async(map, reg, mask, val, change)\ + regmap_update_bits_base(map, reg, mask, val, change, true, false) + +#define regmap_field_write(field, val) \ + regmap_field_update_bits_base(field, ~0, val, NULL, false, false) +#define regmap_field_force_write(field, val) \ + regmap_field_update_bits_base(field, ~0, val, NULL, false, true) +#define regmap_field_update_bits(field, mask, val)\ + regmap_field_update_bits_base(field, mask, val, NULL, false, false) +#define regmap_field_force_update_bits(field, mask, val) \ + regmap_field_update_bits_base(field, mask, val, NULL, false, true) + +#define regmap_fields_write(field, id, val) \ + regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, false) +#define regmap_fields_force_write(field, id, val) \ + regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, true) +#define regmap_fields_update_bits(field, id, mask, val)\ + regmap_fields_update_bits_base(field, id, mask, val, NULL, false, false) +#define regmap_fields_force_update_bits(field, id, mask, val) \ + regmap_fields_update_bits_base(field, id, mask, val, NULL, false, true) + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -691,18 +718,11 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count); -int regmap_update_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val); +int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); -int regmap_update_bits_async(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val); -int regmap_update_bits_check(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change); -int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change); int regmap_get_val_bytes(struct regmap *map); int regmap_get_max_register(struct regmap *map); int regmap_get_reg_stride(struct regmap *map); @@ -770,18 +790,14 @@ struct regmap_field *devm_regmap_field_alloc(struct device *dev, void devm_regmap_field_free(struct device *dev, struct regmap_field *field); int regmap_field_read(struct regmap_field *field, unsigned int *val); -int regmap_field_write(struct regmap_field *field, unsigned int val); -int regmap_field_update_bits(struct regmap_field *field, - unsigned int mask, unsigned int val); - -int regmap_fields_write(struct regmap_field *field, unsigned int id, - unsigned int val); -int regmap_fields_force_write(struct regmap_field *field, unsigned int id, - unsigned int val); +int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); int regmap_fields_read(struct regmap_field *field, unsigned int id, unsigned int *val); -int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, - unsigned int mask, unsigned int val); +int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); /** * Description of an IRQ for the generic regmap irq_chip. @@ -937,8 +953,9 @@ static inline int regmap_bulk_read(struct regmap *map, unsigned int reg, return -EINVAL; } -static inline int regmap_update_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val) +static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) { WARN_ONCE(1, "regmap API is disabled"); return -EINVAL; @@ -951,28 +968,18 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg, return -EINVAL; } -static inline int regmap_update_bits_async(struct regmap *map, - unsigned int reg, - unsigned int mask, unsigned int val) +static inline int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) { WARN_ONCE(1, "regmap API is disabled"); return -EINVAL; } -static inline int regmap_update_bits_check(struct regmap *map, - unsigned int reg, - unsigned int mask, unsigned int val, - bool *change) -{ - WARN_ONCE(1, "regmap API is disabled"); - return -EINVAL; -} - -static inline int regmap_update_bits_check_async(struct regmap *map, - unsigned int reg, - unsigned int mask, - unsigned int val, - bool *change) +static inline int regmap_fields_update_bits_base(struct regmap_field *field, + unsigned int id, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) { WARN_ONCE(1, "regmap API is disabled"); return -EINVAL; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b414a92b677a..649e92a252ae 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -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 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d10418ccd181..185a712a7fe7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -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 diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 30c6de62ae6c..f0e6c06e89ac 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -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) { diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h index 31da16265f2b..d66f8847b676 100644 --- a/sound/soc/codecs/rt298.h +++ b/sound/soc/codecs/rt298.h @@ -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 diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c new file mode 100644 index 000000000000..879bf60f4965 --- /dev/null +++ b/sound/soc/codecs/rt5514.c @@ -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"); diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h new file mode 100644 index 000000000000..6ad8a612f659 --- /dev/null +++ b/sound/soc/codecs/rt5514.h @@ -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__ */ diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 1c10d8ed39d2..f527b5b2817b 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> @@ -53,6 +54,7 @@ static const struct reg_sequence init_list[] = { {RT5616_PR_BASE + 0x21, 0x4040}, {RT5616_PR_BASE + 0x23, 0x0004}, }; + #define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5616_reg[] = { @@ -143,6 +145,7 @@ struct rt5616_priv { struct snd_soc_codec *codec; struct delayed_work patch_work; struct regmap *regmap; + struct clk *mclk; int sysclk; int sysclk_src; @@ -162,9 +165,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg) for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { if (reg >= rt5616_ranges[i].range_min && - reg <= rt5616_ranges[i].range_max) { + reg <= rt5616_ranges[i].range_max) return true; - } } switch (reg) { @@ -190,9 +192,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg) for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { if (reg >= rt5616_ranges[i].range_min && - reg <= rt5616_ranges[i].range_max) { + reg <= rt5616_ranges[i].range_max) return true; - } } switch (reg) { @@ -307,45 +308,47 @@ static unsigned int bst_tlv[] = { static const struct snd_kcontrol_new rt5616_snd_controls[] = { /* Headphone Output Volume */ SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL, + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1, - RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, - 175, 0, dac_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 175, 0, dac_vol_tlv), /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2, - RT5616_BST_SFT1, 8, 0, bst_tlv), + RT5616_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2, - RT5616_BST_SFT2, 8, 0, bst_tlv), + RT5616_BST_SFT2, 8, 0, bst_tlv), /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL, - RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, - 31, 1, in_vol_tlv), + RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, + 31, 1, in_vol_tlv), /* ADC Digital Volume Control */ SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, - 127, 0, adc_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 127, 0, adc_vol_tlv), /* ADC Boost Volume Control */ SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL, - RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, - 3, 0, adc_bst_tlv), + RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), }; static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) + struct snd_soc_dapm_widget *sink) { unsigned int val; @@ -462,20 +465,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = { }; static int rt5616_adc_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); break; default: @@ -486,7 +489,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w, } static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -494,54 +497,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* depop parameters */ snd_soc_update_bits(codec, RT5616_DEPOP_M2, - RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); + RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | - RT5616_HP_CB_MASK, RT5616_HP_CP_PU | - RT5616_HP_SG_DIS | RT5616_HP_CB_PU); + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, RT5616_HP_CP_PU | + RT5616_HP_SG_DIS | RT5616_HP_CB_PU); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_HP_DCC_INT1, 0x9f00); + RT5616_HP_DCC_INT1, 0x9f00); /* headphone amp power on */ snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); + RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); snd_soc_update_bits(codec, RT5616_PWR_VOL, - RT5616_PWR_HV_L | RT5616_PWR_HV_R, - RT5616_PWR_HV_L | RT5616_PWR_HV_R); + RT5616_PWR_HV_L | RT5616_PWR_HV_R, + RT5616_PWR_HV_L | RT5616_PWR_HV_R); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_HP_L | RT5616_PWR_HP_R | - RT5616_PWR_HA, RT5616_PWR_HP_L | - RT5616_PWR_HP_R | RT5616_PWR_HA); + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, RT5616_PWR_HP_L | + RT5616_PWR_HP_R | RT5616_PWR_HA); msleep(50); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); snd_soc_update_bits(codec, RT5616_CHARGE_PUMP, - RT5616_PM_HP_MASK, RT5616_PM_HP_HV); + RT5616_PM_HP_MASK, RT5616_PM_HP_HV); snd_soc_update_bits(codec, RT5616_PR_BASE + - RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); + RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, - RT5616_HP_CO_EN | RT5616_HP_SG_EN); + RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, + RT5616_HP_CO_EN | RT5616_HP_SG_EN); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PR_BASE + - RT5616_CHOP_DAC_ADC, 0x0200, 0x0); + RT5616_CHOP_DAC_ADC, 0x0200, 0x0); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | - RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); /* headphone amp power down */ snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK | - RT5616_HP_CO_MASK | RT5616_HP_CP_MASK | - RT5616_HP_SG_MASK | RT5616_HP_CB_MASK, - RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | - RT5616_HP_CO_DIS | RT5616_HP_CP_PD | - RT5616_HP_SG_EN | RT5616_HP_CB_PD); + RT5616_SMT_TRIG_MASK | + RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK | + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, + RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | + RT5616_HP_CO_DIS | RT5616_HP_CP_PD | + RT5616_HP_SG_EN | RT5616_HP_CB_PD); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_HP_L | RT5616_PWR_HP_R | - RT5616_PWR_HA, 0); + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, 0); break; default: return 0; @@ -551,7 +555,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, } static int rt5616_hp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -559,57 +563,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* headphone unmute sequence */ snd_soc_update_bits(codec, RT5616_DEPOP_M3, - RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | - RT5616_CP_FQ3_MASK, - (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) | - (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) | - (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT)); + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_MAMP_INT_REG2, 0xfc00); + RT5616_MAMP_INT_REG2, 0xfc00); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); + RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTN_MASK, RT5616_RSTN_EN); + RT5616_RSTN_MASK, RT5616_RSTN_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | - RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); snd_soc_update_bits(codec, RT5616_HP_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); msleep(100); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | - RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); msleep(20); snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET, - RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); + RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); break; case SND_SOC_DAPM_PRE_PMD: /* headphone mute sequence */ snd_soc_update_bits(codec, RT5616_DEPOP_M3, - RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | - RT5616_CP_FQ3_MASK, - (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) | - (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) | - (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT)); + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_MAMP_INT_REG2, 0xfc00); + RT5616_MAMP_INT_REG2, 0xfc00); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK, RT5616_HP_SG_EN); + RT5616_HP_SG_MASK, RT5616_HP_SG_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTP_MASK, RT5616_RSTP_EN); + RT5616_RSTP_MASK, RT5616_RSTP_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | - RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET, - RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); + RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); msleep(90); snd_soc_update_bits(codec, RT5616_HP_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); msleep(30); break; @@ -621,24 +625,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w, } static int rt5616_lout_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_LM, RT5616_PWR_LM); + RT5616_PWR_LM, RT5616_PWR_LM); snd_soc_update_bits(codec, RT5616_LOUT_CTRL1, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_LOUT_CTRL1, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_LM, 0); + RT5616_PWR_LM, 0); break; default: @@ -649,19 +653,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w, } static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); + RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST1_OP2, 0); + RT5616_PWR_BST1_OP2, 0); break; default: @@ -672,19 +676,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, } static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); + RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST2_OP2, 0); + RT5616_PWR_BST2_OP2, 0); break; default: @@ -696,13 +700,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2, - RT5616_PWR_PLL_BIT, 0, NULL, 0), + RT5616_PWR_PLL_BIT, 0, NULL, 0), /* Input Side */ /* micbias */ SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1, - RT5616_PWR_LDO_BIT, 0, NULL, 0), + RT5616_PWR_LDO_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2, - RT5616_PWR_MB1_BIT, 0, NULL, 0), + RT5616_PWR_MB1_BIT, 0, NULL, 0), /* Input Lines */ SND_SOC_DAPM_INPUT("MIC1"), @@ -714,45 +718,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { /* Boost */ SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2, - RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2, - RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), /* Input Volume */ SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), /* REC Mixer */ SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0, - rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), + rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0, - rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), + rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), /* ADCs */ SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1, - RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1, - RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), /* ADC Mixer */ SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2, - RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), + RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)), + rt5616_sto1_adc_l_mix, + ARRAY_SIZE(rt5616_sto1_adc_l_mix)), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)), + rt5616_sto1_adc_r_mix, + ARRAY_SIZE(rt5616_sto1_adc_r_mix)), /* Digital Interface */ SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1, - RT5616_PWR_I2S1_BIT, 0, NULL, 0), + RT5616_PWR_I2S1_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -770,68 +776,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { /* Output Side */ /* DAC mixer before sound effect */ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), + rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), + rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2, - RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), + RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), /* DAC Mixer */ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)), + rt5616_sto_dac_l_mix, + ARRAY_SIZE(rt5616_sto_dac_l_mix)), SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)), + rt5616_sto_dac_r_mix, + ARRAY_SIZE(rt5616_sto_dac_r_mix)), /* DACs */ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1, - RT5616_PWR_DAC_L1_BIT, 0), + RT5616_PWR_DAC_L1_BIT, 0), SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1, - RT5616_PWR_DAC_R1_BIT, 0), + RT5616_PWR_DAC_R1_BIT, 0), /* OUT Mixer */ SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT, - 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), + 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT, - 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), + 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), /* Output Volume */ SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL, - RT5616_PWR_OV_L_BIT, 0, NULL, 0), + RT5616_PWR_OV_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL, - RT5616_PWR_OV_R_BIT, 0, NULL, 0), + RT5616_PWR_OV_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL, - RT5616_PWR_HV_L_BIT, 0, NULL, 0), + RT5616_PWR_HV_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL, - RT5616_PWR_HV_R_BIT, 0, NULL, 0), + RT5616_PWR_HV_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL, - RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL, - RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL, - RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL, - RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), /* HPO/LOUT/Mono Mixer */ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, - rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), + rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, - rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), + rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, - rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMU), + rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, - rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMU), + rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0, - rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), + rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), /* Output Lines */ SND_SOC_DAPM_OUTPUT("HPOL"), @@ -950,7 +958,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = { }; static int rt5616_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; @@ -977,7 +986,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", rt5616->bclk[dai->id], rt5616->lrck[dai->id]); dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", - bclk_ms, pre_div, dai->id); + bclk_ms, pre_div, dai->id); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -998,10 +1007,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, mask_clk = RT5616_I2S_PD1_MASK; val_clk = pre_div << RT5616_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5616_I2S1_SDP, - RT5616_I2S_DL_MASK, val_len); + RT5616_I2S_DL_MASK, val_len); snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk); - return 0; } @@ -1050,15 +1058,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } snd_soc_update_bits(codec, RT5616_I2S1_SDP, - RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | - RT5616_I2S_DF_MASK, reg_val); - + RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | + RT5616_I2S_DF_MASK, reg_val); return 0; } static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) + int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); @@ -1078,8 +1085,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); return -EINVAL; } + snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_SCLK_SRC_MASK, reg_val); + RT5616_SCLK_SRC_MASK, reg_val); rt5616->sysclk = freq; rt5616->sysclk_src = clk_id; @@ -1089,7 +1097,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, } static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out) + unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); @@ -1106,19 +1114,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, rt5616->pll_in = 0; rt5616->pll_out = 0; snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK); + RT5616_SCLK_SRC_MASK, + RT5616_SCLK_SRC_MCLK); return 0; } switch (source) { case RT5616_PLL1_S_MCLK: snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK); + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_MCLK); break; case RT5616_PLL1_S_BCLK1: case RT5616_PLL1_S_BCLK2: snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1); + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_BCLK1); break; default: dev_err(codec->dev, "Unknown PLL source %d\n", source); @@ -1136,10 +1147,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, pll_code.n_code, pll_code.k_code); snd_soc_write(codec, RT5616_PLL_CTRL1, - pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); + pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); snd_soc_write(codec, RT5616_PLL_CTRL2, - (pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT | - pll_code.m_bp << RT5616_PLL_M_BP_SFT); + (pll_code.m_bp ? 0 : pll_code.m_code) << + RT5616_PLL_M_SFT | + pll_code.m_bp << RT5616_PLL_M_BP_SFT); rt5616->pll_in = freq_in; rt5616->pll_out = freq_out; @@ -1149,22 +1161,50 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, } static int rt5616_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) + enum snd_soc_bias_level level) { + struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { + + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(rt5616->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5616->mclk); + } else { + ret = clk_prepare_enable(rt5616->mclk); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2); + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); mdelay(10); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); snd_soc_update_bits(codec, RT5616_D_MISC, - RT5616_D_GATE_EN, RT5616_D_GATE_EN); + RT5616_D_GATE_EN, + RT5616_D_GATE_EN); } break; @@ -1189,6 +1229,11 @@ static int rt5616_probe(struct snd_soc_codec *codec) { struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); + /* Check if MCLK provided */ + rt5616->mclk = devm_clk_get(codec->dev, "mclk"); + if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + rt5616->codec = codec; return 0; @@ -1218,11 +1263,10 @@ static int rt5616_resume(struct snd_soc_codec *codec) #define rt5616_resume NULL #endif -#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000 #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) - struct snd_soc_dai_ops rt5616_aif_dai_ops = { .hw_params = rt5616_hw_params, .set_fmt = rt5616_set_dai_fmt, @@ -1296,15 +1340,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match); #endif static int rt5616_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct rt5616_priv *rt5616; unsigned int val; int ret; rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv), - GFP_KERNEL); - if (rt5616 == NULL) + GFP_KERNEL); + if (!rt5616) return -ENOMEM; i2c_set_clientdata(i2c, rt5616); @@ -1326,14 +1370,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c, } regmap_write(rt5616->regmap, RT5616_RESET, 0); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2); + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); mdelay(10); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); ret = regmap_register_patch(rt5616->regmap, init_list, ARRAY_SIZE(init_list)); @@ -1341,11 +1385,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c, dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); + RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616, - rt5616_dai, ARRAY_SIZE(rt5616_dai)); - + rt5616_dai, ARRAY_SIZE(rt5616_dai)); } static int rt5616_i2c_remove(struct i2c_client *i2c) @@ -1361,7 +1404,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client) regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8); regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8); - } static struct i2c_driver rt5616_i2c_driver = { diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 6561c4cc2edd..2f8e20416bd3 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -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", }, {}, }; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 5a806da89f42..100781e37848 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -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; diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 6d3ef366d536..606399de684d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) return (0x6 + ws) << 8; } +static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int target_rate, + unsigned int *target_val, + unsigned int *target_en) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, step; + unsigned int val, en; + unsigned int min, diff; + unsigned int sel_rate[] = { + clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ + adg->rbga_rate_for_441khz, /* 0011: RBGA */ + adg->rbgb_rate_for_48khz, /* 0100: RBGB */ + }; + + min = ~0; + val = 0; + en = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + idx = 0; + step = 2; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(target_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + en = 1 << (sel + 1); /* fixme */ + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return; + } + + *target_val = val; + if (target_en) + *target_en = en; +} + +static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate, + u32 *in, u32 *out, u32 *en) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + unsigned int target_rate; + u32 *target_val; + u32 _in; + u32 _out; + u32 _en; + + /* default = SSI WS */ + _in = + _out = rsnd_adg_ssi_ws_timing_gen2(io); + + target_rate = 0; + target_val = NULL; + _en = 0; + if (runtime->rate != in_rate) { + target_rate = out_rate; + target_val = &_out; + } else if (runtime->rate != out_rate) { + target_rate = in_rate; + target_val = &_in; + } + + if (target_rate) + __rsnd_adg_get_timesel_ratio(priv, io, + target_rate, + target_val, &_en); + + if (in) + *in = _in; + if (out) + *out = _out; + if (en) + *en = _en; +} + int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, struct rsnd_dai_stream *io) { @@ -100,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, int shift = (id % 2) ? 16 : 0; u32 mask, val; - val = rsnd_adg_ssi_ws_timing_gen2(io); + rsnd_adg_get_timesel_ratio(priv, io, + rsnd_src_get_in_rate(priv, io), + rsnd_src_get_out_rate(priv, io), + NULL, &val, NULL); val = val << shift; mask = 0xffff << shift; @@ -110,25 +215,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - u32 timsel) +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int is_play = rsnd_io_is_play(io); + u32 in, out; + u32 mask, en; int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; - u32 mask, ws; - u32 in, out; rsnd_mod_confirm_src(src_mod); - ws = rsnd_adg_ssi_ws_timing_gen2(io); - - in = (is_play) ? timsel : ws; - out = (is_play) ? ws : timsel; + rsnd_adg_get_timesel_ratio(priv, io, + in_rate, out_rate, + &in, &out, &en); in = in << shift; out = out << shift; @@ -157,91 +261,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, break; } - return 0; -} - -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct device *dev = rsnd_priv_to_dev(priv); - int idx, sel, div, step, ret; - u32 val, en; - unsigned int min, diff; - unsigned int sel_rate [] = { - clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ - clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ - clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ - adg->rbga_rate_for_441khz, /* 0011: RBGA */ - adg->rbgb_rate_for_48khz, /* 0100: RBGB */ - }; - - rsnd_mod_confirm_src(src_mod); - - min = ~0; - val = 0; - en = 0; - for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { - idx = 0; - step = 2; - - if (!sel_rate[sel]) - continue; - - for (div = 2; div <= 98304; div += step) { - diff = abs(src_rate - sel_rate[sel] / div); - if (min > diff) { - val = (sel << 8) | idx; - min = diff; - en = 1 << (sel + 1); /* fixme */ - } - - /* - * step of 0_0000 / 0_0001 / 0_1101 - * are out of order - */ - if ((idx > 2) && (idx % 2)) - step *= 2; - if (idx == 0x1c) { - div += step; - step *= 2; - } - idx++; - } - } - - if (min == ~0) { - dev_err(dev, "no Input clock\n"); - return -EIO; - } - - ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); - if (ret < 0) { - dev_err(dev, "timsel error\n"); - return ret; - } - - rsnd_mod_bset(adg_mod, DIV_EN, en, en); - - dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); + if (en) + rsnd_mod_bset(adg_mod, DIV_EN, en, en); return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io) -{ - u32 val = rsnd_adg_ssi_ws_timing_gen2(io); - - rsnd_mod_confirm_src(src_mod); - - return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); -} - static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); @@ -518,13 +543,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv) return -ENOMEM; } - /* - * ADG is special module. - * Use ADG mod without rsnd_mod_init() to make debug easy - * for rsnd_write/rsnd_read - */ - adg->mod.ops = &adg_ops; - adg->mod.priv = priv; + rsnd_mod_init(priv, &adg->mod, &adg_ops, + NULL, NULL, 0, 0); rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index cd1f064e63c4..abb5eaac854a 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -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; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 02b4b085b8d7..3351a701c60e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + return &mod->status; +} + int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id) + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id) { int ret = clk_prepare(clk); @@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv, mod->type = type; mod->clk = clk; mod->priv = priv; + mod->get_status = get_status; return ret; } @@ -163,6 +174,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod) { if (mod->clk) clk_unprepare(mod->clk); + mod->clk = NULL; } void rsnd_mod_interrupt(struct rsnd_mod *mod, @@ -212,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io) return rdai->slots_num; } -int rsnd_get_slot_width(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int chan = runtime->channels; - /* Multi channel Mode */ - if (rsnd_ssi_multi_slaves(io)) + return runtime->channels; +} + +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) +{ + int chan = rsnd_runtime_channel_original(io); + struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); + + if (ctu_mod) { + u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod); + + if (converted_chan) + return converted_chan; + } + + return chan; +} + +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) +{ + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + /* Use Multi SSI */ + if (rsnd_runtime_is_ssi_multi(io)) chan /= rsnd_get_slot_num(io); /* TDM Extend Mode needs 8ch */ @@ -228,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io) return chan; } +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) +{ + int slots = rsnd_get_slot_num(io); + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + return (chan >= 6) && (slots > 1); +} + +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) +{ + return rsnd_runtime_channel_for_ssi(io) >= 6; +} + /* * ADINR function */ @@ -249,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) return 0; } -u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct device *dev = rsnd_priv_to_dev(priv); - u32 chan = runtime->channels; - - switch (chan) { - case 1: - case 2: - case 4: - case 6: - case 8: - break; - default: - dev_warn(dev, "not supported channel\n"); - chan = 0; - break; - } - - return chan; -} - /* * DALIGN function */ @@ -324,31 +351,73 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct rsnd_mod *mod = (io)->mod[idx]; \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 *status = (io)->mod_status + idx; \ + u32 *status = mod->get_status(io, mod, idx); \ u32 mask = 0xF << __rsnd_mod_shift_##func; \ u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ - *status = (*status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ + if (add == 0xF) \ + call = 0; \ + else \ + *status = (*status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), \ *status, call ? #func : ""); \ if (call) \ ret = (mod)->ops->func(mod, io, param); \ + if (ret) \ + dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \ ret; \ }) +static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { + { + /* CAPTURE */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIU, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + }, { + /* PLAYBACK */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + RSND_MOD_SSIU, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + }, +}; + #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ + int type, is_play = rsnd_io_is_play(io); \ int ret = 0, i; \ for (i = 0; i < RSND_MOD_MAX; i++) { \ - mod = (io)->mod[i]; \ + type = rsnd_mod_sequence[is_play][i]; \ + mod = (io)->mod[type]; \ if (!mod) \ continue; \ - ret |= rsnd_mod_call(i, io, fn, param); \ + ret |= rsnd_mod_call(type, io, fn, param); \ } \ ret; \ }) @@ -363,6 +432,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod, if (!mod) return -EIO; + if (io->mod[type] == mod) + return 0; + if (io->mod[type]) return -EINVAL; @@ -511,9 +583,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = rsnd_dai_call(start, io, priv); if (ret < 0) goto dai_trigger_end; + + ret = rsnd_dai_call(irq, io, priv, 1); + if (ret < 0) + goto dai_trigger_end; + break; case SNDRV_PCM_TRIGGER_STOP: - ret = rsnd_dai_call(stop, io, priv); + ret = rsnd_dai_call(irq, io, priv, 0); + + ret |= rsnd_dai_call(stop, io, priv); ret |= rsnd_dai_call(quit, io, priv); @@ -863,7 +942,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } } - if (change) + if (change && cfg->update) cfg->update(cfg->io, mod); return change; @@ -923,7 +1002,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, int ch_size, u32 max) { - if (ch_size > RSND_DVC_CHANNELS) + if (ch_size > RSND_MAX_CHANNELS) return -EINVAL; _cfg->cfg.max = max; @@ -1055,7 +1134,6 @@ static int rsnd_probe(struct platform_device *pdev) struct rsnd_priv *priv; struct device *dev = &pdev->dev; struct rsnd_dai *rdai; - const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); int (*probe_func[])(struct rsnd_priv *priv) = { rsnd_gen_probe, rsnd_dma_probe, @@ -1081,7 +1159,7 @@ static int rsnd_probe(struct platform_device *pdev) } priv->pdev = pdev; - priv->flags = (unsigned long)of_id->data; + priv->flags = (unsigned long)of_device_get_match_data(dev); spin_lock_init(&priv->lock); /* diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index d53a225d19e9..9dcc1f9db026 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -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_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) { - rsnd_mod_write(mod, CTU_CTUIR, enable); + 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_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_ctu_activation(mod); - 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; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 418e6fdd06a3..7658e8fd7bdc 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -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); - if (ret < 0) - return ERR_PTR(ret); + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod), - rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), - rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + *dma_mod = rsnd_mod_get(dma); - ret = attach(io, dma, id, mod_from, mod_to); - if (ret < 0) - return ERR_PTR(ret); + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + rsnd_mod_get_status, type, dma_id); + if (ret < 0) + return ret; - ret = rsnd_dai_connect(dma_mod, io, type); + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + + ret = attach(io, dma, id, mod_from, mod_to); + if (ret < 0) + return ret; + } + + ret = rsnd_dai_connect(*dma_mod, io, type); if (ret < 0) - return ERR_PTR(ret); + return ret; - return rsnd_mod_get(dma); + return 0; } int rsnd_dma_probe(struct rsnd_priv *priv) diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d45ffe496397..02d971f69eff 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -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; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ea24247eba73..46c0ba7b6414 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -104,23 +104,6 @@ void rsnd_write(struct rsnd_priv *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; - regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", @@ -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), diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 65542b6a89e9..195fc7bb22af 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -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; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 317dd793149a..fc89a67258ca 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -86,8 +86,43 @@ enum rsnd_reg { RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */ RSND_REG_CMD_ROUTE_SLCT, RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */ + RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, + RSND_REG_CTU_CPMDR, + RSND_REG_CTU_SCMDR, + RSND_REG_CTU_SV00R, + RSND_REG_CTU_SV01R, + RSND_REG_CTU_SV02R, + RSND_REG_CTU_SV03R, + RSND_REG_CTU_SV04R, + RSND_REG_CTU_SV05R, + RSND_REG_CTU_SV06R, + RSND_REG_CTU_SV07R, + RSND_REG_CTU_SV10R, + RSND_REG_CTU_SV11R, + RSND_REG_CTU_SV12R, + RSND_REG_CTU_SV13R, + RSND_REG_CTU_SV14R, + RSND_REG_CTU_SV15R, + RSND_REG_CTU_SV16R, + RSND_REG_CTU_SV17R, + RSND_REG_CTU_SV20R, + RSND_REG_CTU_SV21R, + RSND_REG_CTU_SV22R, + RSND_REG_CTU_SV23R, + RSND_REG_CTU_SV24R, + RSND_REG_CTU_SV25R, + RSND_REG_CTU_SV26R, + RSND_REG_CTU_SV27R, + RSND_REG_CTU_SV30R, + RSND_REG_CTU_SV31R, + RSND_REG_CTU_SV32R, + RSND_REG_CTU_SV33R, + RSND_REG_CTU_SV34R, + RSND_REG_CTU_SV35R, + RSND_REG_CTU_SV36R, + RSND_REG_CTU_SV37R, RSND_REG_MIX_SWRSR, RSND_REG_MIX_MIXIR, RSND_REG_MIX_ADINR, @@ -147,8 +182,6 @@ struct rsnd_dai_stream; rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) #define rsnd_mod_write(m, r, d) \ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) -#define rsnd_mod_force_write(m, r, d) \ - rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) @@ -160,14 +193,13 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); -u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* * R-Car DMA */ -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id); +int rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id); int rsnd_dma_probe(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); @@ -214,6 +246,9 @@ struct rsnd_mod_ops { int (*stop)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + int (*irq)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, int enable); int (*pcm_new)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); @@ -233,47 +268,54 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; + u32 *(*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + u32 status; }; /* * status * - * 0xH0000CBA + * 0xH0000CB0 * - * A 0: probe 1: remove * B 0: init 1: quit * C 0: start 1: stop * * H is always called (see __rsnd_mod_call) + * H 0: probe 1: remove * H 0: pcm_new * H 0: fallback * H 0: hw_params */ -#define __rsnd_mod_shift_probe 0 -#define __rsnd_mod_shift_remove 0 #define __rsnd_mod_shift_init 4 #define __rsnd_mod_shift_quit 4 #define __rsnd_mod_shift_start 8 #define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_probe 28 /* always called */ +#define __rsnd_mod_shift_remove 28 /* always called */ +#define __rsnd_mod_shift_irq 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ -#define __rsnd_mod_add_probe 1 -#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_probe 0 +#define __rsnd_mod_add_remove 0 #define __rsnd_mod_add_init 1 #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 #define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_irq 0 #define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 -#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_remove 0 #define __rsnd_mod_call_init 0 #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 #define __rsnd_mod_call_stop 1 +#define __rsnd_mod_call_irq 0 #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 @@ -286,10 +328,13 @@ struct rsnd_mod { int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id); + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id); void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, @@ -297,6 +342,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), struct device_node *node, @@ -306,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, void rsnd_set_slot(struct rsnd_dai *rdai, int slots, int slots_total); int rsnd_get_slot(struct rsnd_dai_stream *io); -int rsnd_get_slot_width(struct rsnd_dai_stream *io); int rsnd_get_slot_num(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); + /* * R-Car sound DAI */ @@ -319,7 +373,7 @@ struct rsnd_dai_stream { struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; - u32 mod_status[RSND_MOD_MAX]; + u32 parent_ssi_status; int byte_pos; int period_pos; int byte_per_period; @@ -392,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_probe(struct rsnd_priv *priv); void rsnd_adg_remove(struct rsnd_priv *priv); -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate); -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io); + unsigned int in_rate, + unsigned int out_rate); int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io); @@ -498,10 +550,10 @@ struct rsnd_kctrl_cfg { struct snd_kcontrol *kctrl; }; -#define RSND_DVC_CHANNELS 8 +#define RSND_MAX_CHANNELS 8 struct rsnd_kctrl_cfg_m { struct rsnd_kctrl_cfg cfg; - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; }; struct rsnd_kctrl_cfg_s { @@ -547,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io); +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) @@ -573,9 +625,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv); int rsnd_src_probe(struct rsnd_priv *priv); void rsnd_src_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime); + +#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) +#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in); + #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") #define rsnd_parse_connect_src(rdai, playback, capture) \ @@ -588,6 +644,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); +int rsnd_ctu_converted_channel(struct rsnd_mod *mod); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu") diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 8a357fdf1077..1bc7ecfc42a9 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -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) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 5eda056d9f20..15d6ffe8be74 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -25,7 +25,6 @@ struct rsnd_src { struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ u32 convert_rate; /* sampling rate convert */ - int err; int irq; }; @@ -34,7 +33,7 @@ struct rsnd_src { #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) #define rsnd_src_to_dma(src) ((src)->dma) #define rsnd_src_nr(priv) ((priv)->src_nr) -#define rsnd_enable_sync_convert(src) ((src)->sen.val) +#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) @@ -94,15 +93,16 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, } static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, - struct rsnd_src *src) + struct rsnd_mod *mod) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate; if (!runtime) return 0; - if (!rsnd_enable_sync_convert(src)) + if (!rsnd_src_sync_is_enabled(mod)) return src->convert_rate; convert_rate = src->sync.val; @@ -116,23 +116,33 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, return convert_rate; } -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct rsnd_src *src; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); unsigned int rate = 0; + int is_play = rsnd_io_is_play(io); - if (src_mod) { - src = rsnd_mod_to_src(src_mod); + /* + * + * Playback + * runtime_rate -> [SRC] -> convert_rate + * + * Capture + * convert_rate -> [SRC] -> runtime_rate + */ - /* - * return convert rate if SRC is used, - * otherwise, return runtime->rate as usual - */ - rate = rsnd_src_convert_rate(io, src); - } + if (is_play == is_in) + return runtime->rate; + + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + if (src_mod) + rate = rsnd_src_convert_rate(io, src_mod); if (!rate) rate = runtime->rate; @@ -179,8 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); + u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; u32 bsdsr, bsisr; @@ -189,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, if (!runtime) return; + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!convert_rate) + if (fin == fout) ratio = 0; - else if (convert_rate > runtime->rate) - ratio = 100 * convert_rate / runtime->rate; + else if (fin > fout) + ratio = 100 * fin / fout; else - ratio = 100 * runtime->rate / convert_rate; + ratio = 100 * fout / fin; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); @@ -206,16 +218,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, * SRC_ADINR */ adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_original(io); /* * SRC_IFSCR / SRC_IFSVR */ ifscr = 0; fsrate = 0; - if (convert_rate) { + if (fin != fout) { ifscr = 1; - fsrate = 0x0400000 / convert_rate * runtime->rate; + fsrate = 0x0400000 / fout * fin; } /* @@ -223,10 +235,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ cr = 0x00011110; route = 0x0; - if (convert_rate) { + if (fin != fout) { route = 0x1; - if (rsnd_enable_sync_convert(src)) { + if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; route |= rsnd_io_is_play(io) ? (0x1 << 24) : (0x1 << 25); @@ -250,6 +262,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, break; } + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); @@ -259,22 +273,17 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_BSISR, bsisr); rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - if (convert_rate) - rsnd_adg_set_convert_clk_gen2(mod, io, - runtime->rate, - convert_rate); - else - rsnd_adg_set_convert_timing_gen2(mod, io); + rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); } -#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1) -#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0) -static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) +static int rsnd_src_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; @@ -298,14 +307,16 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) sys_int_val = sys_int_val & 0xffff; rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); + + return 0; } static void rsnd_src_status_clear(struct rsnd_mod *mod) @@ -316,9 +327,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); } -static bool rsnd_src_record_error(struct rsnd_mod *mod) +static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; bool ret = false; @@ -327,18 +337,14 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod) /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) val0 = val0 & 0xffff; if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || - (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - - src->err++; + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) ret = true; - } return ret; } @@ -347,7 +353,6 @@ static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val; /* @@ -355,7 +360,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, * * Enable SRC output if you want to use sync convert together with DVC */ - val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ? + val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -367,11 +372,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - /* - * stop SRC output only - * see rsnd_src_quit - */ - rsnd_mod_write(mod, SRC_CTRL, 0x01); + rsnd_mod_write(mod, SRC_CTRL, 0); return 0; } @@ -390,10 +391,6 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_status_clear(mod); - rsnd_src_irq_enable(mod); - - src->err = 0; - /* reset sync convert_rate */ src->sync.val = 0; @@ -405,21 +402,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - rsnd_src_irq_disable(mod); - - /* stop both out/in */ - rsnd_mod_write(mod, SRC_CTRL, 0); rsnd_src_halt(mod); rsnd_mod_power_off(mod); - if (src->err) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); - src->convert_rate = 0; /* reset sync convert_rate */ @@ -432,8 +419,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); + bool stop = false; spin_lock(&priv->lock); @@ -441,26 +427,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_out; - if (rsnd_src_record_error(mod)) { - - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - rsnd_src_stop(mod, io, priv); - rsnd_src_start(mod, io, priv); - } - - if (src->err > 1024) { - rsnd_src_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (rsnd_src_error_occurred(mod)) + stop = true; rsnd_src_status_clear(mod); rsnd_src_interrupt_out: spin_unlock(&priv->lock); + + if (stop) + snd_pcm_stop_xrun(io->substream); } static irqreturn_t rsnd_src_interrupt(int irq, void *data) @@ -485,7 +461,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, /* * IRQ is not supported on non-DT * see - * rsnd_src_irq_enable() + * rsnd_src_irq() */ ret = devm_request_irq(dev, irq, rsnd_src_interrupt, @@ -495,9 +471,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, return ret; } - src->dma = rsnd_dma_attach(io, mod, 0); - if (IS_ERR(src->dma)) - return PTR_ERR(src->dma); + ret = rsnd_dma_attach(io, mod, &src->dma, 0); return ret; } @@ -506,8 +480,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -516,15 +488,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, */ /* - * SRC sync convert needs clock master - */ - if (!rsnd_rdai_is_clk_master(rdai)) - return 0; - - /* - * SRC In doesn't work if DVC was enabled + * It can't use SRC Synchronous convert + * when Capture if it uses CMD */ - 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; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 7ee89da4dd5f..5f848f054745 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -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"); -} - -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; + dev_warn(dev, "%s[%d] status check failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); } -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; - - ret = rsnd_ssi_config_init(ssi, io); - if (ret < 0) - return ret; + if (!rsnd_ssi_is_parent(mod, io)) + rsnd_ssi_config_init(mod, io); - 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,61 +434,43 @@ 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, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +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) +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; + /* - * no limit to start + * don't stop if not last user * see also - * rsnd_ssi_stop + * rsnd_ssi_start * rsnd_ssi_interrupt */ - return __rsnd_ssi_start(mod, io, priv); -} - -static int __rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; + if (ssi->usrcnt > 1) + return 0; /* * disable all IRQ, @@ -532,33 +492,38 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 val = 0; - /* - * don't stop if not last user - * see also - * rsnd_ssi_start - * rsnd_ssi_interrupt - */ - if (ssi->usrcnt > 1) + if (rsnd_is_gen1(priv)) return 0; - return __rsnd_ssi_stop(mod, io, priv); + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); + + return 0; } static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; + bool stop = false; spin_lock(&priv->lock); @@ -566,7 +531,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; - status = rsnd_ssi_record_error(ssi); + status = rsnd_ssi_status_get(mod); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -588,23 +553,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, } /* DMA only */ - if (is_dma && (status & (UIRQ | OIRQ))) { - /* - * restart SSI - */ - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - __rsnd_ssi_stop(mod, io, priv); - __rsnd_ssi_start(mod, io, priv); - } - - if (ssi->err > 1024) { - rsnd_ssi_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (is_dma && (status & (UIRQ | OIRQ))) + stop = true; rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: @@ -612,6 +562,10 @@ rsnd_ssi_interrupt_out: if (elapsed) rsnd_dai_period_elapsed(io); + + if (stop) + snd_pcm_stop_xrun(io->substream); + } static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) @@ -627,12 +581,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) * SSI PIO */ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) + struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + if (!__rsnd_ssi_is_pin_sharing(mod)) return; + if (!rsnd_rdai_is_clk_master(rdai)) + return; + switch (rsnd_mod_id(mod)) { case 1: case 2: @@ -647,6 +606,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, } } +static int rsnd_ssi_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + /* + * rsnd_rdai_is_clk_master() will be enabled after set_fmt, + * and, pcm_new will be called after it. + * This function reuse pcm_new at this point. + */ + rsnd_ssi_parent_attach(mod, io); + + return 0; +} + static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -662,7 +635,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, if (rsnd_ssi_is_multi_slave(mod, io)) return 0; - rsnd_ssi_parent_attach(mod, io, priv); + /* + * It can't judge ssi parent at this point + * see rsnd_ssi_pcm_new() + */ ret = rsnd_ssiu_attach(io, mod); if (ret < 0) @@ -683,6 +659,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; @@ -705,9 +683,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, if (ret) return ret; - ssi->dma = rsnd_dma_attach(io, mod, dma_id); - if (IS_ERR(ssi->dma)) - return PTR_ERR(ssi->dma); + /* SSI probe might be called many times in MUX multi path */ + ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); return ret; } @@ -772,6 +749,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; @@ -858,6 +837,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } +static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(io, mod, type); +} + int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; @@ -920,7 +934,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - RSND_MOD_SSI, i); + rsnd_ssi_get_status, RSND_MOD_SSI, i); if (ret) goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 06d72828e5bc..6f9b388ec5a8 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -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; } |