diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/st,sta350.txt | 107 | ||||
-rw-r--r-- | include/sound/soc-dapm.h | 1 | ||||
-rw-r--r-- | include/sound/soc.h | 54 | ||||
-rw-r--r-- | include/sound/sta350.h | 52 | ||||
-rw-r--r-- | include/trace/events/asoc.h | 92 | ||||
-rw-r--r-- | sound/soc/codecs/88pm860x-codec.c | 12 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/cq93vc.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/mc13783.c | 14 | ||||
-rw-r--r-- | sound/soc/codecs/si476x.c | 14 | ||||
-rw-r--r-- | sound/soc/codecs/sta350.c | 1266 | ||||
-rw-r--r-- | sound/soc/codecs/sta350.h | 228 | ||||
-rw-r--r-- | sound/soc/codecs/wm5102.c | 12 | ||||
-rw-r--r-- | sound/soc/codecs/wm5110.c | 12 | ||||
-rw-r--r-- | sound/soc/codecs/wm8350.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/wm8400.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/wm8994.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/wm8997.c | 13 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 576 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 91 | ||||
-rw-r--r-- | sound/soc/soc-io.c | 332 |
22 files changed, 2369 insertions, 554 deletions
diff --git a/Documentation/devicetree/bindings/sound/st,sta350.txt b/Documentation/devicetree/bindings/sound/st,sta350.txt new file mode 100644 index 000000000000..950188891abd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,sta350.txt @@ -0,0 +1,107 @@ +STA350 audio CODEC + +The driver for this device only supports I2C. + +Required properties: + + - compatible: "st,sta350" + - reg: the I2C address of the device for I2C + - reset-gpios: a GPIO spec for the reset pin. If specified, it will be + deasserted before communication to the codec starts. + + - power-down-gpios: a GPIO spec for the power down pin. If specified, + it will be deasserted before communication to the codec + starts. + + - vdd-dig-supply: regulator spec, providing 3.3V + - vdd-pll-supply: regulator spec, providing 3.3V + - vcc-supply: regulator spec, providing 5V - 26V + +Optional properties: + + - st,output-conf: number, Selects the output configuration: + 0: 2-channel (full-bridge) power, 2-channel data-out + 1: 2 (half-bridge). 1 (full-bridge) on-board power + 2: 2 Channel (Full-Bridge) Power, 1 Channel FFX + 3: 1 Channel Mono-Parallel + If parameter is missing, mode 0 will be enabled. + + - st,ch1-output-mapping: Channel 1 output mapping + - st,ch2-output-mapping: Channel 2 output mapping + - st,ch3-output-mapping: Channel 3 output mapping + 0: Channel 1 + 1: Channel 2 + 2: Channel 3 + If parameter is missing, channel 1 is choosen. + + - st,thermal-warning-recover: + If present, thermal warning recovery is enabled. + + - st,thermal-warning-adjustment: + If present, thermal warning adjustment is enabled. + + - st,fault-detect-recovery: + If present, then fault recovery will be enabled. + + - st,ffx-power-output-mode: string + The FFX power output mode selects how the FFX output timing is + configured. Must be one of these values: + - "drop-compensation" + - "tapered-compensation" + - "full-power-mode" + - "variable-drop-compensation" (default) + + - st,drop-compensation-ns: number + Only required for "st,ffx-power-output-mode" == + "variable-drop-compensation". + Specifies the drop compensation in nanoseconds. + The value must be in the range of 0..300, and only + multiples of 20 are allowed. Default is 140ns. + + - st,overcurrent-warning-adjustment: + If present, overcurrent warning adjustment is enabled. + + - st,max-power-use-mpcc: + If present, then MPCC bits are used for MPC coefficients, + otherwise standard MPC coefficients are used. + + - st,max-power-corr: + If present, power bridge correction for THD reduction near maximum + power output is enabled. + + - st,am-reduction-mode: + If present, FFX mode runs in AM reduction mode, otherwise normal + FFX mode is used. + + - st,odd-pwm-speed-mode: + If present, PWM speed mode run on odd speed mode (341.3 kHz) on all + channels. If not present, normal PWM spped mode (384 kHz) will be used. + + - st,distortion-compensation: + If present, distortion compensation variable uses DCC coefficient. + If not present, preset DC coefficient is used. + + - st,invalid-input-detect-mute: + If not present, automatic invalid input detect mute is enabled. + + + +Example: + +codec: sta350@38 { + compatible = "st,sta350"; + reg = <0x1c>; + reset-gpios = <&gpio1 19 0>; + power-down-gpios = <&gpio1 16 0>; + st,output-conf = <0x3>; // set output to 2-channel + // (full-bridge) power, + // 2-channel data-out + st,ch1-output-mapping = <0>; // set channel 1 output ch 1 + st,ch2-output-mapping = <0>; // set channel 2 output ch 1 + st,ch3-output-mapping = <0>; // set channel 3 output ch 1 + st,max-power-correction; // enables power bridge + // correction for THD reduction + // near maximum power output + st,invalid-input-detect-mute; // mute if no valid digital + // audio signal is provided. +}; diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ef78f562f4a8..75020f52acdd 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -606,6 +606,7 @@ struct snd_soc_dapm_context { enum snd_soc_dapm_type, int); struct device *dev; /* from parent - for debug */ + struct snd_soc_component *component; /* parent component */ struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_platform *platform; /* parent platform */ struct snd_soc_card *card; /* parent card */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 3da5f2328ff3..e84f22f89765 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -400,8 +400,6 @@ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - struct regmap *regmap); int snd_soc_cache_sync(struct snd_soc_codec *codec); int snd_soc_cache_init(struct snd_soc_codec *codec); int snd_soc_cache_exit(struct snd_soc_codec *codec); @@ -681,6 +679,14 @@ struct snd_soc_component { const struct snd_soc_component_driver *driver; struct list_head dai_list; + + int (*read)(struct snd_soc_component *, unsigned int, unsigned int *); + int (*write)(struct snd_soc_component *, unsigned int, unsigned int); + + struct regmap *regmap; + int val_bytes; + + struct mutex io_mutex; }; /* SoC Audio Codec device */ @@ -705,18 +711,14 @@ struct snd_soc_codec { unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int cache_init:1; /* codec cache has been initialized */ - unsigned int using_regmap:1; /* using regmap access */ u32 cache_only; /* Suppress writes to hardware */ u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ hw_write_t hw_write; - unsigned int (*read)(struct snd_soc_codec *, unsigned int); - int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); void *reg_cache; struct mutex cache_rw_mutex; - int val_bytes; /* component */ struct snd_soc_component component; @@ -755,6 +757,7 @@ struct snd_soc_codec_driver { unsigned int freq_in, unsigned int freq_out); /* codec IO */ + struct regmap *(*get_regmap)(struct device *); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); unsigned int reg_cache_size; @@ -832,7 +835,6 @@ struct snd_soc_platform { int id; struct device *dev; const struct snd_soc_platform_driver *driver; - struct mutex mutex; unsigned int suspended:1; /* platform is suspended */ unsigned int probed:1; @@ -1141,6 +1143,22 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); +/* component IO */ +int snd_soc_component_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val); +int snd_soc_component_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val); +int snd_soc_component_update_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val); +int snd_soc_component_update_bits_async(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val); +void snd_soc_component_async_complete(struct snd_soc_component *component); +int snd_soc_component_test_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int value); + +int snd_soc_component_init_io(struct snd_soc_component *component, + struct regmap *regmap); + /* device driver data */ static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, @@ -1245,6 +1263,22 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) } /** + * snd_soc_kcontrol_component() - Returns the component that registered the + * control + * @kcontrol: The control for which to get the component + * + * Note: This function will work correctly if the control has been registered + * for a component. Either with snd_soc_add_codec_controls() or + * snd_soc_add_platform_controls() or via table based setup for either a + * CODEC, a platform or component driver. Otherwise the behavior is undefined. + */ +static inline struct snd_soc_component *snd_soc_kcontrol_component( + struct snd_kcontrol *kcontrol) +{ + return snd_kcontrol_chip(kcontrol); +} + +/** * snd_soc_kcontrol_codec() - Returns the CODEC that registered the control * @kcontrol: The control for which to get the CODEC * @@ -1255,7 +1289,7 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) static inline struct snd_soc_codec *snd_soc_kcontrol_codec( struct snd_kcontrol *kcontrol) { - return snd_kcontrol_chip(kcontrol); + return snd_soc_component_to_codec(snd_soc_kcontrol_component(kcontrol)); } /** @@ -1266,10 +1300,10 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( * registered with snd_soc_add_platform_controls() or via table based setup of * a snd_soc_platform_driver. Otherwise the behavior is undefined. */ -static inline struct snd_soc_codec *snd_soc_kcontrol_platform( +static inline struct snd_soc_platform *snd_soc_kcontrol_platform( struct snd_kcontrol *kcontrol) { - return snd_kcontrol_chip(kcontrol); + return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol)); } int snd_soc_util_init(void); diff --git a/include/sound/sta350.h b/include/sound/sta350.h new file mode 100644 index 000000000000..3a3298106b22 --- /dev/null +++ b/include/sound/sta350.h @@ -0,0 +1,52 @@ +/* + * Platform data for ST STA350 ASoC codec driver. + * + * Copyright: 2014 Raumfeld GmbH + * Author: Sven Brandau <info@brandau.biz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __LINUX_SND__STA350_H +#define __LINUX_SND__STA350_H + +#define STA350_OCFG_2CH 0 +#define STA350_OCFG_2_1CH 1 +#define STA350_OCFG_1CH 3 + +#define STA350_OM_CH1 0 +#define STA350_OM_CH2 1 +#define STA350_OM_CH3 2 + +#define STA350_THERMAL_ADJUSTMENT_ENABLE 1 +#define STA350_THERMAL_RECOVERY_ENABLE 2 +#define STA350_FAULT_DETECT_RECOVERY_BYPASS 1 + +#define STA350_FFX_PM_DROP_COMP 0 +#define STA350_FFX_PM_TAPERED_COMP 1 +#define STA350_FFX_PM_FULL_POWER 2 +#define STA350_FFX_PM_VARIABLE_DROP_COMP 3 + + +struct sta350_platform_data { + u8 output_conf; + u8 ch1_output_mapping; + u8 ch2_output_mapping; + u8 ch3_output_mapping; + u8 ffx_power_output_mode; + u8 drop_compensation_ns; + unsigned int thermal_warning_recovery:1; + unsigned int thermal_warning_adjustment:1; + unsigned int fault_detect_recovery:1; + unsigned int oc_warning_adjustment:1; + unsigned int max_power_use_mpcc:1; + unsigned int max_power_correction:1; + unsigned int am_reduction_mode:1; + unsigned int odd_pwm_speed_mode:1; + unsigned int distortion_compensation:1; + unsigned int invalid_input_detect_mute:1; +}; + +#endif /* __LINUX_SND__STA350_H */ diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 03996b2bb04f..c75c795a377b 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -11,102 +11,10 @@ struct snd_soc_jack; struct snd_soc_codec; -struct snd_soc_platform; struct snd_soc_card; struct snd_soc_dapm_widget; struct snd_soc_dapm_path; -/* - * Log register events - */ -DECLARE_EVENT_CLASS(snd_soc_reg, - - TP_PROTO(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val), - - TP_ARGS(codec, reg, val), - - TP_STRUCT__entry( - __string( name, codec->name ) - __field( int, id ) - __field( unsigned int, reg ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __assign_str(name, codec->name); - __entry->id = codec->id; - __entry->reg = reg; - __entry->val = val; - ), - - TP_printk("codec=%s.%d reg=%x val=%x", __get_str(name), - (int)__entry->id, (unsigned int)__entry->reg, - (unsigned int)__entry->val) -); - -DEFINE_EVENT(snd_soc_reg, snd_soc_reg_write, - - TP_PROTO(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val), - - TP_ARGS(codec, reg, val) - -); - -DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read, - - TP_PROTO(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val), - - TP_ARGS(codec, reg, val) - -); - -DECLARE_EVENT_CLASS(snd_soc_preg, - - TP_PROTO(struct snd_soc_platform *platform, unsigned int reg, - unsigned int val), - - TP_ARGS(platform, reg, val), - - TP_STRUCT__entry( - __string( name, platform->name ) - __field( int, id ) - __field( unsigned int, reg ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __assign_str(name, platform->name); - __entry->id = platform->id; - __entry->reg = reg; - __entry->val = val; - ), - - TP_printk("platform=%s.%d reg=%x val=%x", __get_str(name), - (int)__entry->id, (unsigned int)__entry->reg, - (unsigned int)__entry->val) -); - -DEFINE_EVENT(snd_soc_preg, snd_soc_preg_write, - - TP_PROTO(struct snd_soc_platform *platform, unsigned int reg, - unsigned int val), - - TP_ARGS(platform, reg, val) - -); - -DEFINE_EVENT(snd_soc_preg, snd_soc_preg_read, - - TP_PROTO(struct snd_soc_platform *platform, unsigned int reg, - unsigned int val), - - TP_ARGS(platform, reg, val) - -); - DECLARE_EVENT_CLASS(snd_soc_card, TP_PROTO(struct snd_soc_card *card, int val), diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index b18cafa5a858..3c4b10ff48c1 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1327,10 +1327,6 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x->codec = codec; - ret = snd_soc_codec_set_cache_io(codec, pm860x->regmap); - if (ret) - return ret; - for (i = 0; i < 4; i++) { ret = request_threaded_irq(pm860x->irq[i], NULL, pm860x_codec_handler, IRQF_ONESHOT, @@ -1362,10 +1358,18 @@ static int pm860x_remove(struct snd_soc_codec *codec) return 0; } +static struct regmap *pm860x_get_regmap(struct device *dev) +{ + struct pm860x_priv *pm860x = dev_get_drvdata(dev); + + return pm860x->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_pm860x = { .probe = pm860x_probe, .remove = pm860x_remove, .set_bias_level = pm860x_set_bias_level, + .get_regmap = pm860x_get_regmap, .controls = pm860x_snd_controls, .num_controls = ARRAY_SIZE(pm860x_snd_controls), diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f0e840137887..c7b853f520cf 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -80,6 +80,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SSM2602_SPI if SPI_MASTER select SND_SOC_SSM2602_I2C if I2C select SND_SOC_STA32X if I2C + select SND_SOC_STA350 if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS5086 if I2C @@ -435,6 +436,10 @@ config SND_SOC_SSM2602_I2C config SND_SOC_STA32X tristate +config SND_SOC_STA350 + tristate "STA350 speaker amplifier" + depends on I2C + config SND_SOC_STA529 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3c4d275d064b..efdb4d060201 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -74,6 +74,7 @@ snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o snd-soc-sta32x-objs := sta32x.o +snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o @@ -221,6 +222,7 @@ obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o +obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 1e25c7af853b..5ee48c8e4849 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -139,8 +139,6 @@ static int cq93vc_probe(struct snd_soc_codec *codec) davinci_vc->cq93vc.codec = codec; - snd_soc_codec_set_cache_io(codec, davinci_vc->regmap); - /* Off, with power on */ cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -154,11 +152,19 @@ static int cq93vc_remove(struct snd_soc_codec *codec) return 0; } +static struct regmap *cq93vc_get_regmap(struct device *dev) +{ + struct davinci_vc *davinci_vc = codec->dev->platform_data; + + return davinci_vc->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_cq93vc = { .set_bias_level = cq93vc_set_bias_level, .probe = cq93vc_probe, .remove = cq93vc_remove, .resume = cq93vc_resume, + .get_regmap = cq93vc_get_regmap, .controls = cq93vc_snd_controls, .num_controls = ARRAY_SIZE(cq93vc_snd_controls), }; diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 2c59b1fb69dc..9029e20514e1 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -608,14 +608,6 @@ static struct snd_kcontrol_new mc13783_control_list[] = { static int mc13783_probe(struct snd_soc_codec *codec) { struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); - int ret; - - ret = snd_soc_codec_set_cache_io(codec, - dev_get_regmap(codec->dev->parent, NULL)); - if (ret != 0) { - dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - return ret; - } /* these are the reset values */ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893); @@ -735,9 +727,15 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = { } }; +static struct regmap *mc13783_get_regmap(struct device *dev) +{ + return dev_get_regmap(dev->parent, NULL); +} + static struct snd_soc_codec_driver soc_codec_dev_mc13783 = { .probe = mc13783_probe, .remove = mc13783_remove, + .get_regmap = mc13783_get_regmap, .controls = mc13783_control_list, .num_controls = ARRAY_SIZE(mc13783_control_list), .dapm_widgets = mc13783_dapm_widgets, diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 244c097cd905..f26befb0c297 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -208,13 +208,6 @@ out: return err; } -static int si476x_codec_probe(struct snd_soc_codec *codec) -{ - struct regmap *regmap = dev_get_regmap(codec->dev->parent, NULL); - - return snd_soc_codec_set_cache_io(codec, regmap); -} - static struct snd_soc_dai_ops si476x_dai_ops = { .hw_params = si476x_codec_hw_params, .set_fmt = si476x_codec_set_dai_fmt, @@ -238,8 +231,13 @@ static struct snd_soc_dai_driver si476x_dai = { .ops = &si476x_dai_ops, }; +static struct regmap *si476x_get_regmap(struct device *dev) +{ + return dev_get_regmap(dev->parent, NULL); +} + static struct snd_soc_codec_driver soc_codec_dev_si476x = { - .probe = si476x_codec_probe, + .get_regmap = si476x_get_regmap, .dapm_widgets = si476x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), .dapm_routes = si476x_dapm_routes, diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c new file mode 100644 index 000000000000..12ebbaf5d95f --- /dev/null +++ b/sound/soc/codecs/sta350.c @@ -0,0 +1,1266 @@ +/* + * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system + * + * Copyright: 2014 Raumfeld GmbH + * Author: Sven Brandau <info@brandau.biz> + * + * based on code from: + * Raumfeld GmbH + * Johannes Stezenbach <js@sig21.net> + * Wolfson Microelectronics PLC. + * Mark Brown <broonie@opensource.wolfsonmicro.com> + * Freescale Semiconductor, Inc. + * Timur Tabi <timur@freescale.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/slab.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 <sound/sta350.h> +#include "sta350.h" + +#define STA350_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define STA350_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + +/* Power-up register defaults */ +static const struct reg_default sta350_regs[] = { + { 0x0, 0x63 }, + { 0x1, 0x80 }, + { 0x2, 0xdf }, + { 0x3, 0x40 }, + { 0x4, 0xc2 }, + { 0x5, 0x5c }, + { 0x6, 0x00 }, + { 0x7, 0xff }, + { 0x8, 0x60 }, + { 0x9, 0x60 }, + { 0xa, 0x60 }, + { 0xb, 0x00 }, + { 0xc, 0x00 }, + { 0xd, 0x00 }, + { 0xe, 0x00 }, + { 0xf, 0x40 }, + { 0x10, 0x80 }, + { 0x11, 0x77 }, + { 0x12, 0x6a }, + { 0x13, 0x69 }, + { 0x14, 0x6a }, + { 0x15, 0x69 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x00 }, + { 0x1a, 0x00 }, + { 0x1b, 0x00 }, + { 0x1c, 0x00 }, + { 0x1d, 0x00 }, + { 0x1e, 0x00 }, + { 0x1f, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x2a }, + { 0x28, 0xc0 }, + { 0x29, 0xf3 }, + { 0x2a, 0x33 }, + { 0x2b, 0x00 }, + { 0x2c, 0x0c }, + { 0x31, 0x00 }, + { 0x36, 0x00 }, + { 0x37, 0x00 }, + { 0x38, 0x00 }, + { 0x39, 0x01 }, + { 0x3a, 0xee }, + { 0x3b, 0xff }, + { 0x3c, 0x7e }, + { 0x3d, 0xc0 }, + { 0x3e, 0x26 }, + { 0x3f, 0x00 }, + { 0x48, 0x00 }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x04 }, + { 0x4c, 0x00 }, +}; + +static const struct regmap_range sta350_write_regs_range[] = { + regmap_reg_range(STA350_CONFA, STA350_AUTO2), + regmap_reg_range(STA350_C1CFG, STA350_FDRC2), + regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), + regmap_reg_range(STA350_NSHAPE, STA350_MISC2), +}; + +static const struct regmap_range sta350_read_regs_range[] = { + regmap_reg_range(STA350_CONFA, STA350_AUTO2), + regmap_reg_range(STA350_C1CFG, STA350_STATUS), + regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), + regmap_reg_range(STA350_NSHAPE, STA350_MISC2), +}; + +static const struct regmap_range sta350_volatile_regs_range[] = { + regmap_reg_range(STA350_CFADDR2, STA350_CFUD), + regmap_reg_range(STA350_STATUS, STA350_STATUS), +}; + +static const struct regmap_access_table sta350_write_regs = { + .yes_ranges = sta350_write_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_write_regs_range), +}; + +static const struct regmap_access_table sta350_read_regs = { + .yes_ranges = sta350_read_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_read_regs_range), +}; + +static const struct regmap_access_table sta350_volatile_regs = { + .yes_ranges = sta350_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_volatile_regs_range), +}; + +/* regulator power supply names */ +static const char * const sta350_supply_names[] = { + "vdd-dig", /* digital supply, 3.3V */ + "vdd-pll", /* pll supply, 3.3V */ + "vcc" /* power amp supply, 5V - 26V */ +}; + +/* codec private data */ +struct sta350_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[ARRAY_SIZE(sta350_supply_names)]; + struct sta350_platform_data *pdata; + + unsigned int mclk; + unsigned int format; + + u32 coef_shadow[STA350_COEF_COUNT]; + int shutdown; + + struct gpio_desc *gpiod_nreset; + struct gpio_desc *gpiod_power_down; + + struct mutex coeff_lock; +}; + +static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1); +static const DECLARE_TLV_DB_SCALE(tone_tlv, -1200, 200, 0); + +static const char * const sta350_drc_ac[] = { + "Anti-Clipping", "Dynamic Range Compression" +}; +static const char * const sta350_auto_gc_mode[] = { + "User", "AC no clipping", "AC limited clipping (10%)", + "DRC nighttime listening mode" +}; +static const char * const sta350_auto_xo_mode[] = { + "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", + "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", + "340Hz", "360Hz" +}; +static const char * const sta350_binary_output[] = { + "FFX 3-state output - normal operation", "Binary output" +}; +static const char * const sta350_limiter_select[] = { + "Limiter Disabled", "Limiter #1", "Limiter #2" +}; +static const char * const sta350_limiter_attack_rate[] = { + "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024", + "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752", + "0.0645", "0.0564", "0.0501", "0.0451" +}; +static const char * const sta350_limiter_release_rate[] = { + "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", + "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", + "0.0134", "0.0117", "0.0110", "0.0104" +}; +static const char * const sta350_noise_shaper_type[] = { + "Third order", "Fourth order" +}; + +static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), + 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), + 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), + 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), + 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), + 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), + 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), +); + +static SOC_ENUM_SINGLE_DECL(sta350_drc_ac_enum, + STA350_CONFD, STA350_CONFD_DRC_SHIFT, + sta350_drc_ac); +static SOC_ENUM_SINGLE_DECL(sta350_noise_shaper_enum, + STA350_CONFE, STA350_CONFE_NSBW_SHIFT, + sta350_noise_shaper_type); +static SOC_ENUM_SINGLE_DECL(sta350_auto_gc_enum, + STA350_AUTO1, STA350_AUTO1_AMGC_SHIFT, + sta350_auto_gc_mode); +static SOC_ENUM_SINGLE_DECL(sta350_auto_xo_enum, + STA350_AUTO2, STA350_AUTO2_XO_SHIFT, + sta350_auto_xo_mode); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch1_enum, + STA350_C1CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch2_enum, + STA350_C2CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch3_enum, + STA350_C3CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch1_enum, + STA350_C1CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch2_enum, + STA350_C2CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch3_enum, + STA350_C3CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter1_attack_rate_enum, + STA350_L1AR, STA350_LxA_SHIFT, + sta350_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter2_attack_rate_enum, + STA350_L2AR, STA350_LxA_SHIFT, + sta350_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter1_release_rate_enum, + STA350_L1AR, STA350_LxR_SHIFT, + sta350_limiter_release_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter2_release_rate_enum, + STA350_L2AR, STA350_LxR_SHIFT, + sta350_limiter_release_rate); + +/* + * byte array controls for setting biquad, mixer, scaling coefficients; + * for biquads all five coefficients need to be set in one go, + * mixer and pre/postscale coefs can be set individually; + * each coef is 24bit, the bytes are ordered in the same way + * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0) + */ + +static int sta350_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoef = kcontrol->private_value >> 16; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 3 * numcoef; + return 0; +} + +static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; + int i, ret = 0; + + mutex_lock(&sta350->coeff_lock); + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); + if (numcoef == 1) { + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); + } else if (numcoef == 5) { + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); + } else { + ret = -EINVAL; + goto exit_unlock; + } + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +exit_unlock: + mutex_unlock(&sta350->coeff_lock); + + return ret; +} + +static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud; + int i; + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); + for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++) + sta350->coef_shadow[index + i] = + (ucontrol->value.bytes.data[3 * i] << 16) + | (ucontrol->value.bytes.data[3 * i + 1] << 8) + | (ucontrol->value.bytes.data[3 * i + 2]); + for (i = 0; i < 3 * numcoef; i++) + regmap_write(sta350->regmap, STA350_B1CF1 + i, + ucontrol->value.bytes.data[i]); + if (numcoef == 1) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); + else if (numcoef == 5) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02); + else + return -EINVAL; + + return 0; +} + +static int sta350_sync_coef_shadow(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + unsigned int cfud; + int i; + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + + for (i = 0; i < STA350_COEF_COUNT; i++) { + regmap_write(sta350->regmap, STA350_CFADDR2, i); + regmap_write(sta350->regmap, STA350_B1CF1, + (sta350->coef_shadow[i] >> 16) & 0xff); + regmap_write(sta350->regmap, STA350_B1CF2, + (sta350->coef_shadow[i] >> 8) & 0xff); + regmap_write(sta350->regmap, STA350_B1CF3, + (sta350->coef_shadow[i]) & 0xff); + /* + * chip documentation does not say if the bits are + * self-clearing, so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); + } + return 0; +} + +static int sta350_cache_sync(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + unsigned int mute; + int rc; + + /* mute during register sync */ + regmap_read(sta350->regmap, STA350_CFUD, &mute); + regmap_write(sta350->regmap, STA350_MMUTE, mute | STA350_MMUTE_MMUTE); + sta350_sync_coef_shadow(codec); + rc = regcache_sync(sta350->regmap); + regmap_write(sta350->regmap, STA350_MMUTE, mute); + return rc; +} + +#define SINGLE_COEF(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta350_coefficient_info, \ + .get = sta350_coefficient_get,\ + .put = sta350_coefficient_put, \ + .private_value = index | (1 << 16) } + +#define BIQUAD_COEFS(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta350_coefficient_info, \ + .get = sta350_coefficient_get,\ + .put = sta350_coefficient_put, \ + .private_value = index | (5 << 16) } + +static const struct snd_kcontrol_new sta350_snd_controls[] = { +SOC_SINGLE_TLV("Master Volume", STA350_MVOL, 0, 0xff, 1, mvol_tlv), +/* VOL */ +SOC_SINGLE_TLV("Ch1 Volume", STA350_C1VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch2 Volume", STA350_C2VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch3 Volume", STA350_C3VOL, 0, 0xff, 1, chvol_tlv), +/* CONFD */ +SOC_SINGLE("High Pass Filter Bypass Switch", + STA350_CONFD, STA350_CONFD_HPB_SHIFT, 1, 1), +SOC_SINGLE("De-emphasis Filter Switch", + STA350_CONFD, STA350_CONFD_DEMP_SHIFT, 1, 0), +SOC_SINGLE("DSP Bypass Switch", + STA350_CONFD, STA350_CONFD_DSPB_SHIFT, 1, 0), +SOC_SINGLE("Post-scale Link Switch", + STA350_CONFD, STA350_CONFD_PSL_SHIFT, 1, 0), +SOC_SINGLE("Biquad Coefficient Link Switch", + STA350_CONFD, STA350_CONFD_BQL_SHIFT, 1, 0), +SOC_ENUM("Compressor/Limiter Switch", sta350_drc_ac_enum), +SOC_ENUM("Noise Shaper Bandwidth", sta350_noise_shaper_enum), +SOC_SINGLE("Zero-detect Mute Enable Switch", + STA350_CONFD, STA350_CONFD_ZDE_SHIFT, 1, 0), +SOC_SINGLE("Submix Mode Switch", + STA350_CONFD, STA350_CONFD_SME_SHIFT, 1, 0), +/* CONFE */ +SOC_SINGLE("Zero Cross Switch", STA350_CONFE, STA350_CONFE_ZCE_SHIFT, 1, 0), +SOC_SINGLE("Soft Ramp Switch", STA350_CONFE, STA350_CONFE_SVE_SHIFT, 1, 0), +/* MUTE */ +SOC_SINGLE("Master Switch", STA350_MMUTE, STA350_MMUTE_MMUTE_SHIFT, 1, 1), +SOC_SINGLE("Ch1 Switch", STA350_MMUTE, STA350_MMUTE_C1M_SHIFT, 1, 1), +SOC_SINGLE("Ch2 Switch", STA350_MMUTE, STA350_MMUTE_C2M_SHIFT, 1, 1), +SOC_SINGLE("Ch3 Switch", STA350_MMUTE, STA350_MMUTE_C3M_SHIFT, 1, 1), +/* AUTOx */ +SOC_ENUM("Automode GC", sta350_auto_gc_enum), +SOC_ENUM("Automode XO", sta350_auto_xo_enum), +/* CxCFG */ +SOC_SINGLE("Ch1 Tone Control Bypass Switch", + STA350_C1CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Tone Control Bypass Switch", + STA350_C2CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch1 EQ Bypass Switch", + STA350_C1CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 EQ Bypass Switch", + STA350_C2CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch1 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch3 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_ENUM("Ch1 Binary Output Select", sta350_binary_output_ch1_enum), +SOC_ENUM("Ch2 Binary Output Select", sta350_binary_output_ch2_enum), +SOC_ENUM("Ch3 Binary Output Select", sta350_binary_output_ch3_enum), +SOC_ENUM("Ch1 Limiter Select", sta350_limiter_ch1_enum), +SOC_ENUM("Ch2 Limiter Select", sta350_limiter_ch2_enum), +SOC_ENUM("Ch3 Limiter Select", sta350_limiter_ch3_enum), +/* TONE */ +SOC_SINGLE_RANGE_TLV("Bass Tone Control Volume", + STA350_TONE, STA350_TONE_BTC_SHIFT, 1, 13, 0, tone_tlv), +SOC_SINGLE_RANGE_TLV("Treble Tone Control Volume", + STA350_TONE, STA350_TONE_TTC_SHIFT, 1, 13, 0, tone_tlv), +SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta350_limiter1_attack_rate_enum), +SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta350_limiter2_attack_rate_enum), +SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta350_limiter1_release_rate_enum), +SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta350_limiter2_release_rate_enum), + +/* + * depending on mode, the attack/release thresholds have + * two different enum definitions; provide both + */ +SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", + STA350_L1ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", + STA350_L2ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", + STA350_L1ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", + STA350_L2ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", + STA350_L1ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", + STA350_L2ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", + STA350_L1ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_drc_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", + STA350_L2ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_drc_release_tlv), + +BIQUAD_COEFS("Ch1 - Biquad 1", 0), +BIQUAD_COEFS("Ch1 - Biquad 2", 5), +BIQUAD_COEFS("Ch1 - Biquad 3", 10), +BIQUAD_COEFS("Ch1 - Biquad 4", 15), +BIQUAD_COEFS("Ch2 - Biquad 1", 20), +BIQUAD_COEFS("Ch2 - Biquad 2", 25), +BIQUAD_COEFS("Ch2 - Biquad 3", 30), +BIQUAD_COEFS("Ch2 - Biquad 4", 35), +BIQUAD_COEFS("High-pass", 40), +BIQUAD_COEFS("Low-pass", 45), +SINGLE_COEF("Ch1 - Prescale", 50), +SINGLE_COEF("Ch2 - Prescale", 51), +SINGLE_COEF("Ch1 - Postscale", 52), +SINGLE_COEF("Ch2 - Postscale", 53), +SINGLE_COEF("Ch3 - Postscale", 54), +SINGLE_COEF("Thermal warning - Postscale", 55), +SINGLE_COEF("Ch1 - Mix 1", 56), +SINGLE_COEF("Ch1 - Mix 2", 57), +SINGLE_COEF("Ch2 - Mix 1", 58), +SINGLE_COEF("Ch2 - Mix 2", 59), +SINGLE_COEF("Ch3 - Mix 1", 60), +SINGLE_COEF("Ch3 - Mix 2", 61), +}; + +static const struct snd_soc_dapm_widget sta350_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LEFT"), +SND_SOC_DAPM_OUTPUT("RIGHT"), +SND_SOC_DAPM_OUTPUT("SUB"), +}; + +static const struct snd_soc_dapm_route sta350_dapm_routes[] = { + { "LEFT", NULL, "DAC" }, + { "RIGHT", NULL, "DAC" }, + { "SUB", NULL, "DAC" }, + { "DAC", NULL, "Playback" }, +}; + +/* MCLK interpolation ratio per fs */ +static struct { + int fs; + int ir; +} interpolation_ratios[] = { + { 32000, 0 }, + { 44100, 0 }, + { 48000, 0 }, + { 88200, 1 }, + { 96000, 1 }, + { 176400, 2 }, + { 192000, 2 }, +}; + +/* MCLK to fs clock ratios */ +static int mcs_ratio_table[3][6] = { + { 768, 512, 384, 256, 128, 576 }, + { 384, 256, 192, 128, 64, 0 }, + { 192, 128, 96, 64, 32, 0 }, +}; + +/** + * sta350_set_dai_sysclk - configure MCLK + * @codec_dai: the codec DAI + * @clk_id: the clock ID (ignored) + * @freq: the MCLK input frequency + * @dir: the clock direction (ignored) + * + * The value of MCLK is used to determine which sample rates are supported + * by the STA350, based on the mcs_ratio_table. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + */ +static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "mclk=%u\n", freq); + sta350->mclk = freq; + + return 0; +} + +/** + * sta350_set_dai_fmt - configure the codec for the selected audio format + * @codec_dai: the codec DAI + * @fmt: a SND_SOC_DAIFMT_x value indicating the data format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + */ +static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + unsigned int confb = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + sta350->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + confb |= STA350_CONFB_C2IM; + break; + case SND_SOC_DAIFMT_NB_IF: + confb |= STA350_CONFB_C1IM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(sta350->regmap, STA350_CONFB, + STA350_CONFB_C1IM | STA350_CONFB_C2IM, confb); +} + +/** + * sta350_hw_params - program the STA350 with the given hardware parameters. + * @substream: the audio stream + * @params: the hardware parameters to set + * @dai: the SOC DAI (ignored) + * + * This function programs the hardware with the values provided. + * Specifically, the sample rate and the data format. + */ +static int sta350_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 sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int i, mcs = -EINVAL, ir = -EINVAL; + unsigned int confa, confb; + unsigned int rate, ratio; + int ret; + + if (!sta350->mclk) { + dev_err(codec->dev, + "sta350->mclk is unset. Unable to determine ratio\n"); + return -EIO; + } + + rate = params_rate(params); + ratio = sta350->mclk / rate; + dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio); + + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { + if (interpolation_ratios[i].fs == rate) { + ir = interpolation_ratios[i].ir; + break; + } + } + + if (ir < 0) { + dev_err(codec->dev, "Unsupported samplerate: %u\n", rate); + return -EINVAL; + } + + for (i = 0; i < 6; i++) { + if (mcs_ratio_table[ir][i] == ratio) { + mcs = i; + break; + } + } + + if (mcs < 0) { + dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio); + return -EINVAL; + } + + confa = (ir << STA350_CONFA_IR_SHIFT) | + (mcs << STA350_CONFA_MCS_SHIFT); + confb = 0; + + switch (params_width(params)) { + case 24: + dev_dbg(codec->dev, "24bit\n"); + /* fall through */ + case 32: + dev_dbg(codec->dev, "24bit or 32bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x2; + break; + } + + break; + case 20: + dev_dbg(codec->dev, "20bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x4; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x5; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x6; + break; + } + + break; + case 18: + dev_dbg(codec->dev, "18bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x8; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x9; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xa; + break; + } + + break; + case 16: + dev_dbg(codec->dev, "16bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0xd; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xe; + break; + } + + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(sta350->regmap, STA350_CONFA, + STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK, + confa); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sta350->regmap, STA350_CONFB, + STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB, + confb); + if (ret < 0) + return ret; + + return 0; +} + +static int sta350_startup_sequence(struct sta350_priv *sta350) +{ + if (sta350->gpiod_power_down) + gpiod_set_value(sta350->gpiod_power_down, 1); + + if (sta350->gpiod_nreset) { + gpiod_set_value(sta350->gpiod_nreset, 0); + mdelay(1); + gpiod_set_value(sta350->gpiod_nreset, 1); + mdelay(1); + } + + return 0; +} + +/** + * sta350_set_bias_level - DAPM callback + * @codec: the codec device + * @level: DAPM power level + * + * This is called by ALSA to put the codec into low power mode + * or to wake it up. If the codec is powered off completely + * all registers must be restored after power on. + */ +static int sta350_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int ret; + + dev_dbg(codec->dev, "level = %d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, + STA350_CONFF_PWDN | STA350_CONFF_EAPD); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable( + ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(codec->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + sta350_startup_sequence(sta350); + sta350_cache_sync(codec); + } + + /* Power down */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, + 0); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0); + + /* power down: low */ + if (sta350->gpiod_power_down) + gpiod_set_value(sta350->gpiod_power_down, 0); + + if (sta350->gpiod_nreset) + gpiod_set_value(sta350->gpiod_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), + sta350->supplies); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static const struct snd_soc_dai_ops sta350_dai_ops = { + .hw_params = sta350_hw_params, + .set_sysclk = sta350_set_dai_sysclk, + .set_fmt = sta350_set_dai_fmt, +}; + +static struct snd_soc_dai_driver sta350_dai = { + .name = "sta350-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA350_RATES, + .formats = STA350_FORMATS, + }, + .ops = &sta350_dai_ops, +}; + +#ifdef CONFIG_PM +static int sta350_suspend(struct snd_soc_codec *codec) +{ + sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int sta350_resume(struct snd_soc_codec *codec) +{ + sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define sta350_suspend NULL +#define sta350_resume NULL +#endif + +static int sta350_probe(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + struct sta350_platform_data *pdata = sta350->pdata; + int i, ret = 0, thermal = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = sta350_startup_sequence(sta350); + if (ret < 0) { + dev_err(codec->dev, "Failed to startup device\n"); + return ret; + } + + /* CONFA */ + if (!pdata->thermal_warning_recovery) + thermal |= STA350_CONFA_TWAB; + if (!pdata->thermal_warning_adjustment) + thermal |= STA350_CONFA_TWRB; + if (!pdata->fault_detect_recovery) + thermal |= STA350_CONFA_FDRB; + regmap_update_bits(sta350->regmap, STA350_CONFA, + STA350_CONFA_TWAB | STA350_CONFA_TWRB | + STA350_CONFA_FDRB, + thermal); + + /* CONFC */ + regmap_update_bits(sta350->regmap, STA350_CONFC, + STA350_CONFC_OM_MASK, + pdata->ffx_power_output_mode + << STA350_CONFC_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_CONFC, + STA350_CONFC_CSZ_MASK, + pdata->drop_compensation_ns + << STA350_CONFC_CSZ_SHIFT); + regmap_update_bits(sta350->regmap, + STA350_CONFC, + STA350_CONFC_OCRB, + pdata->oc_warning_adjustment ? + STA350_CONFC_OCRB : 0); + + /* CONFE */ + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_MPCV, + pdata->max_power_use_mpcc ? + STA350_CONFE_MPCV : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_MPC, + pdata->max_power_correction ? + STA350_CONFE_MPC : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_AME, + pdata->am_reduction_mode ? + STA350_CONFE_AME : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_PWMS, + pdata->odd_pwm_speed_mode ? + STA350_CONFE_PWMS : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_DCCV, + pdata->distortion_compensation ? + STA350_CONFE_DCCV : 0); + /* CONFF */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_IDE, + pdata->invalid_input_detect_mute ? + STA350_CONFF_IDE : 0); + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_OCFG_MASK, + pdata->output_conf + << STA350_CONFF_OCFG_SHIFT); + + /* channel to output mapping */ + regmap_update_bits(sta350->regmap, STA350_C1CFG, + STA350_CxCFG_OM_MASK, + pdata->ch1_output_mapping + << STA350_CxCFG_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_C2CFG, + STA350_CxCFG_OM_MASK, + pdata->ch2_output_mapping + << STA350_CxCFG_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_C3CFG, + STA350_CxCFG_OM_MASK, + pdata->ch3_output_mapping + << STA350_CxCFG_OM_SHIFT); + + /* initialize coefficient shadow RAM with reset values */ + for (i = 4; i <= 49; i += 5) + sta350->coef_shadow[i] = 0x400000; + for (i = 50; i <= 54; i++) + sta350->coef_shadow[i] = 0x7fffff; + sta350->coef_shadow[55] = 0x5a9df7; + sta350->coef_shadow[56] = 0x7fffff; + sta350->coef_shadow[59] = 0x7fffff; + sta350->coef_shadow[60] = 0x400000; + sta350->coef_shadow[61] = 0x400000; + + sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); + + return 0; +} + +static int sta350_remove(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + + sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); + + return 0; +} + +static const struct snd_soc_codec_driver sta350_codec = { + .probe = sta350_probe, + .remove = sta350_remove, + .suspend = sta350_suspend, + .resume = sta350_resume, + .set_bias_level = sta350_set_bias_level, + .controls = sta350_snd_controls, + .num_controls = ARRAY_SIZE(sta350_snd_controls), + .dapm_widgets = sta350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets), + .dapm_routes = sta350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes), +}; + +static const struct regmap_config sta350_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = STA350_MISC2, + .reg_defaults = sta350_regs, + .num_reg_defaults = ARRAY_SIZE(sta350_regs), + .cache_type = REGCACHE_RBTREE, + .wr_table = &sta350_write_regs, + .rd_table = &sta350_read_regs, + .volatile_table = &sta350_volatile_regs, +}; + +#ifdef CONFIG_OF +static const struct of_device_id st350_dt_ids[] = { + { .compatible = "st,sta350", }, + { } +}; +MODULE_DEVICE_TABLE(of, st350_dt_ids); + +static const char * const sta350_ffx_modes[] = { + [STA350_FFX_PM_DROP_COMP] = "drop-compensation", + [STA350_FFX_PM_TAPERED_COMP] = "tapered-compensation", + [STA350_FFX_PM_FULL_POWER] = "full-power-mode", + [STA350_FFX_PM_VARIABLE_DROP_COMP] = "variable-drop-compensation", +}; + +static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) +{ + struct device_node *np = dev->of_node; + struct sta350_platform_data *pdata; + const char *ffx_power_mode; + u16 tmp; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_property_read_u8(np, "st,output-conf", + &pdata->output_conf); + of_property_read_u8(np, "st,ch1-output-mapping", + &pdata->ch1_output_mapping); + of_property_read_u8(np, "st,ch2-output-mapping", + &pdata->ch2_output_mapping); + of_property_read_u8(np, "st,ch3-output-mapping", + &pdata->ch3_output_mapping); + + if (of_get_property(np, "st,thermal-warning-recovery", NULL)) + pdata->thermal_warning_recovery = 1; + if (of_get_property(np, "st,thermal-warning-adjustment", NULL)) + pdata->thermal_warning_adjustment = 1; + if (of_get_property(np, "st,fault-detect-recovery", NULL)) + pdata->fault_detect_recovery = 1; + + pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP; + if (!of_property_read_string(np, "st,ffx-power-output-mode", + &ffx_power_mode)) { + int i, mode = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++) + if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i])) + mode = i; + + if (mode < 0) + dev_warn(dev, "Unsupported ffx output mode: %s\n", + ffx_power_mode); + else + pdata->ffx_power_output_mode = mode; + } + + tmp = 140; + of_property_read_u16(np, "st,drop-compensation-ns", &tmp); + pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20; + + if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL)) + pdata->oc_warning_adjustment = 1; + + /* CONFE */ + if (of_get_property(np, "st,max-power-use-mpcc", NULL)) + pdata->max_power_use_mpcc = 1; + + if (of_get_property(np, "st,max-power-correction", NULL)) + pdata->max_power_correction = 1; + + if (of_get_property(np, "st,am-reduction-mode", NULL)) + pdata->am_reduction_mode = 1; + + if (of_get_property(np, "st,odd-pwm-speed-mode", NULL)) + pdata->odd_pwm_speed_mode = 1; + + if (of_get_property(np, "st,distortion-compensation", NULL)) + pdata->distortion_compensation = 1; + + /* CONFF */ + if (of_get_property(np, "st,invalid-input-detect-mute", NULL)) + pdata->invalid_input_detect_mute = 1; + + sta350->pdata = pdata; + + return 0; +} +#endif + +static int sta350_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct sta350_priv *sta350; + int ret, i; + + sta350 = devm_kzalloc(dev, sizeof(struct sta350_priv), GFP_KERNEL); + if (!sta350) + return -ENOMEM; + + mutex_init(&sta350->coeff_lock); + sta350->pdata = dev_get_platdata(dev); + +#ifdef CONFIG_OF + if (dev->of_node) { + ret = sta350_probe_dt(dev, sta350); + if (ret < 0) + return ret; + } +#endif + + /* GPIOs */ + sta350->gpiod_nreset = devm_gpiod_get(dev, "reset"); + if (IS_ERR(sta350->gpiod_nreset)) { + ret = PTR_ERR(sta350->gpiod_nreset); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + sta350->gpiod_nreset = NULL; + } else { + gpiod_direction_output(sta350->gpiod_nreset, 0); + } + + sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down"); + if (IS_ERR(sta350->gpiod_power_down)) { + ret = PTR_ERR(sta350->gpiod_power_down); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + sta350->gpiod_power_down = NULL; + } else { + gpiod_direction_output(sta350->gpiod_power_down, 0); + } + + /* regulators */ + for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++) + sta350->supplies[i].supply = sta350_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + sta350->regmap = devm_regmap_init_i2c(i2c, &sta350_regmap); + if (IS_ERR(sta350->regmap)) { + ret = PTR_ERR(sta350->regmap); + dev_err(dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, sta350); + + ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1); + if (ret < 0) + dev_err(dev, "Failed to register codec (%d)\n", ret); + + return ret; +} + +static int sta350_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id sta350_i2c_id[] = { + { "sta350", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta350_i2c_id); + +static struct i2c_driver sta350_i2c_driver = { + .driver = { + .name = "sta350", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st350_dt_ids), + }, + .probe = sta350_i2c_probe, + .remove = sta350_i2c_remove, + .id_table = sta350_i2c_id, +}; + +module_i2c_driver(sta350_i2c_driver); + +MODULE_DESCRIPTION("ASoC STA350 driver"); +MODULE_AUTHOR("Sven Brandau <info@brandau.biz>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h new file mode 100644 index 000000000000..c3248f0fad2c --- /dev/null +++ b/sound/soc/codecs/sta350.h @@ -0,0 +1,228 @@ +/* + * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Sven Brandau <info@brandau.biz> + * + * based on code from: + * Raumfeld GmbH + * Johannes Stezenbach <js@sig21.net> + * Wolfson Microelectronics PLC. + * Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef _ASOC_STA_350_H +#define _ASOC_STA_350_H + +/* STA50 register addresses */ + +#define STA350_REGISTER_COUNT 0x4D +#define STA350_COEF_COUNT 62 + +#define STA350_CONFA 0x00 +#define STA350_CONFB 0x01 +#define STA350_CONFC 0x02 +#define STA350_CONFD 0x03 +#define STA350_CONFE 0x04 +#define STA350_CONFF 0x05 +#define STA350_MMUTE 0x06 +#define STA350_MVOL 0x07 +#define STA350_C1VOL 0x08 +#define STA350_C2VOL 0x09 +#define STA350_C3VOL 0x0a +#define STA350_AUTO1 0x0b +#define STA350_AUTO2 0x0c +#define STA350_AUTO3 0x0d +#define STA350_C1CFG 0x0e +#define STA350_C2CFG 0x0f +#define STA350_C3CFG 0x10 +#define STA350_TONE 0x11 +#define STA350_L1AR 0x12 +#define STA350_L1ATRT 0x13 +#define STA350_L2AR 0x14 +#define STA350_L2ATRT 0x15 +#define STA350_CFADDR2 0x16 +#define STA350_B1CF1 0x17 +#define STA350_B1CF2 0x18 +#define STA350_B1CF3 0x19 +#define STA350_B2CF1 0x1a +#define STA350_B2CF2 0x1b +#define STA350_B2CF3 0x1c +#define STA350_A1CF1 0x1d +#define STA350_A1CF2 0x1e +#define STA350_A1CF3 0x1f +#define STA350_A2CF1 0x20 +#define STA350_A2CF2 0x21 +#define STA350_A2CF3 0x22 +#define STA350_B0CF1 0x23 +#define STA350_B0CF2 0x24 +#define STA350_B0CF3 0x25 +#define STA350_CFUD 0x26 +#define STA350_MPCC1 0x27 +#define STA350_MPCC2 0x28 +#define STA350_DCC1 0x29 +#define STA350_DCC2 0x2a +#define STA350_FDRC1 0x2b +#define STA350_FDRC2 0x2c +#define STA350_STATUS 0x2d +/* reserved: 0x2d - 0x30 */ +#define STA350_EQCFG 0x31 +#define STA350_EATH1 0x32 +#define STA350_ERTH1 0x33 +#define STA350_EATH2 0x34 +#define STA350_ERTH2 0x35 +#define STA350_CONFX 0x36 +#define STA350_SVCA 0x37 +#define STA350_SVCB 0x38 +#define STA350_RMS0A 0x39 +#define STA350_RMS0B 0x3a +#define STA350_RMS0C 0x3b +#define STA350_RMS1A 0x3c +#define STA350_RMS1B 0x3d +#define STA350_RMS1C 0x3e +#define STA350_EVOLRES 0x3f +/* reserved: 0x40 - 0x47 */ +#define STA350_NSHAPE 0x48 +#define STA350_CTXB4B1 0x49 +#define STA350_CTXB7B5 0x4a +#define STA350_MISC1 0x4b +#define STA350_MISC2 0x4c + +/* 0x00 CONFA */ +#define STA350_CONFA_MCS_MASK 0x03 +#define STA350_CONFA_MCS_SHIFT 0 +#define STA350_CONFA_IR_MASK 0x18 +#define STA350_CONFA_IR_SHIFT 3 +#define STA350_CONFA_TWRB BIT(5) +#define STA350_CONFA_TWAB BIT(6) +#define STA350_CONFA_FDRB BIT(7) + +/* 0x01 CONFB */ +#define STA350_CONFB_SAI_MASK 0x0f +#define STA350_CONFB_SAI_SHIFT 0 +#define STA350_CONFB_SAIFB BIT(4) +#define STA350_CONFB_DSCKE BIT(5) +#define STA350_CONFB_C1IM BIT(6) +#define STA350_CONFB_C2IM BIT(7) + +/* 0x02 CONFC */ +#define STA350_CONFC_OM_MASK 0x03 +#define STA350_CONFC_OM_SHIFT 0 +#define STA350_CONFC_CSZ_MASK 0x3c +#define STA350_CONFC_CSZ_SHIFT 2 +#define STA350_CONFC_OCRB BIT(7) + +/* 0x03 CONFD */ +#define STA350_CONFD_HPB_SHIFT 0 +#define STA350_CONFD_DEMP_SHIFT 1 +#define STA350_CONFD_DSPB_SHIFT 2 +#define STA350_CONFD_PSL_SHIFT 3 +#define STA350_CONFD_BQL_SHIFT 4 +#define STA350_CONFD_DRC_SHIFT 5 +#define STA350_CONFD_ZDE_SHIFT 6 +#define STA350_CONFD_SME_SHIFT 7 + +/* 0x04 CONFE */ +#define STA350_CONFE_MPCV BIT(0) +#define STA350_CONFE_MPCV_SHIFT 0 +#define STA350_CONFE_MPC BIT(1) +#define STA350_CONFE_MPC_SHIFT 1 +#define STA350_CONFE_NSBW BIT(2) +#define STA350_CONFE_NSBW_SHIFT 2 +#define STA350_CONFE_AME BIT(3) +#define STA350_CONFE_AME_SHIFT 3 +#define STA350_CONFE_PWMS BIT(4) +#define STA350_CONFE_PWMS_SHIFT 4 +#define STA350_CONFE_DCCV BIT(5) +#define STA350_CONFE_DCCV_SHIFT 5 +#define STA350_CONFE_ZCE BIT(6) +#define STA350_CONFE_ZCE_SHIFT 6 +#define STA350_CONFE_SVE BIT(7) +#define STA350_CONFE_SVE_SHIFT 7 + +/* 0x05 CONFF */ +#define STA350_CONFF_OCFG_MASK 0x03 +#define STA350_CONFF_OCFG_SHIFT 0 +#define STA350_CONFF_IDE BIT(2) +#define STA350_CONFF_BCLE BIT(3) +#define STA350_CONFF_LDTE BIT(4) +#define STA350_CONFF_ECLE BIT(5) +#define STA350_CONFF_PWDN BIT(6) +#define STA350_CONFF_EAPD BIT(7) + +/* 0x06 MMUTE */ +#define STA350_MMUTE_MMUTE 0x01 +#define STA350_MMUTE_MMUTE_SHIFT 0 +#define STA350_MMUTE_C1M 0x02 +#define STA350_MMUTE_C1M_SHIFT 1 +#define STA350_MMUTE_C2M 0x04 +#define STA350_MMUTE_C2M_SHIFT 2 +#define STA350_MMUTE_C3M 0x08 +#define STA350_MMUTE_C3M_SHIFT 3 +#define STA350_MMUTE_LOC_MASK 0xC0 +#define STA350_MMUTE_LOC_SHIFT 6 + +/* 0x0b AUTO1 */ +#define STA350_AUTO1_AMGC_MASK 0x30 +#define STA350_AUTO1_AMGC_SHIFT 4 + +/* 0x0c AUTO2 */ +#define STA350_AUTO2_AMAME 0x01 +#define STA350_AUTO2_AMAM_MASK 0x0e +#define STA350_AUTO2_AMAM_SHIFT 1 +#define STA350_AUTO2_XO_MASK 0xf0 +#define STA350_AUTO2_XO_SHIFT 4 + +/* 0x0d AUTO3 */ +#define STA350_AUTO3_PEQ_MASK 0x1f +#define STA350_AUTO3_PEQ_SHIFT 0 + +/* 0x0e 0x0f 0x10 CxCFG */ +#define STA350_CxCFG_TCB_SHIFT 0 +#define STA350_CxCFG_EQBP_SHIFT 1 +#define STA350_CxCFG_VBP_SHIFT 2 +#define STA350_CxCFG_BO_SHIFT 3 +#define STA350_CxCFG_LS_SHIFT 4 +#define STA350_CxCFG_OM_MASK 0xc0 +#define STA350_CxCFG_OM_SHIFT 6 + +/* 0x11 TONE */ +#define STA350_TONE_BTC_SHIFT 0 +#define STA350_TONE_TTC_SHIFT 4 + +/* 0x12 0x13 0x14 0x15 limiter attack/release */ +#define STA350_LxA_SHIFT 0 +#define STA350_LxR_SHIFT 4 + +/* 0x26 CFUD */ +#define STA350_CFUD_W1 0x01 +#define STA350_CFUD_WA 0x02 +#define STA350_CFUD_R1 0x04 +#define STA350_CFUD_RA 0x08 + + +/* biquad filter coefficient table offsets */ +#define STA350_C1_BQ_BASE 0 +#define STA350_C2_BQ_BASE 20 +#define STA350_CH_BQ_NUM 4 +#define STA350_BQ_NUM_COEF 5 +#define STA350_XO_HP_BQ_BASE 40 +#define STA350_XO_LP_BQ_BASE 45 +#define STA350_C1_PRESCALE 50 +#define STA350_C2_PRESCALE 51 +#define STA350_C1_POSTSCALE 52 +#define STA350_C2_POSTSCALE 53 +#define STA350_C3_POSTSCALE 54 +#define STA350_TW_POSTSCALE 55 +#define STA350_C1_MIX1 56 +#define STA350_C1_MIX2 57 +#define STA350_C2_MIX1 58 +#define STA350_C2_MIX2 59 +#define STA350_C3_MIX1 60 +#define STA350_C3_MIX2 61 + +#endif /* _ASOC_STA_350_H */ diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index dcf1d12cfef8..7a046536ea68 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1760,10 +1760,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_codec_set_cache_io(codec, priv->core.arizona->regmap); - if (ret != 0) - return ret; - ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2); if (ret != 0) return ret; @@ -1802,9 +1798,17 @@ static unsigned int wm5102_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_5R, }; +static struct regmap *wm5102_get_regmap(struct device *dev) +{ + struct wm5102_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .probe = wm5102_codec_probe, .remove = wm5102_codec_remove, + .get_regmap = wm5102_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index df5a38dd8328..97eb1bc5bea7 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1589,10 +1589,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) priv->core.arizona->dapm = &codec->dapm; - ret = snd_soc_codec_set_cache_io(codec, priv->core.arizona->regmap); - if (ret != 0) - return ret; - arizona_init_spk(codec); arizona_init_gpio(codec); @@ -1633,9 +1629,17 @@ static unsigned int wm5110_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_6R, }; +static struct regmap *wm5110_get_regmap(struct device *dev) +{ + struct wm5110_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { .probe = wm5110_codec_probe, .remove = wm5110_codec_remove, + .get_regmap = wm5110_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 42a72b2404d5..392285edb595 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1505,8 +1505,6 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; - snd_soc_codec_set_cache_io(codec, wm8350->regmap); - /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); @@ -1608,11 +1606,19 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) return 0; } +static struct regmap *wm8350_get_regmap(struct device *dev) +{ + struct wm8350 *wm8350 = dev_get_platdata(dev); + + return wm8350->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm8350 = { .probe = wm8350_codec_probe, .remove = wm8350_codec_remove, .suspend = wm8350_suspend, .resume = wm8350_resume, + .get_regmap = wm8350_get_regmap, .set_bias_level = wm8350_set_bias_level, .controls = wm8350_snd_controls, diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index edfdbcd9d316..06e913d3fea1 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1318,8 +1318,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) priv->wm8400 = wm8400; priv->codec = codec; - snd_soc_codec_set_cache_io(codec, wm8400->regmap); - ret = devm_regulator_bulk_get(wm8400->dev, ARRAY_SIZE(power), &power[0]); if (ret != 0) { @@ -1361,11 +1359,19 @@ static int wm8400_codec_remove(struct snd_soc_codec *codec) return 0; } +static struct regmap *wm8400_get_regmap(struct device *dev) +{ + struct wm8400 *wm8400 = dev_get_platdata(dev); + + return wm8400->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm8400 = { .probe = wm8400_codec_probe, .remove = wm8400_codec_remove, .suspend = wm8400_suspend, .resume = wm8400_resume, + .get_regmap = wm8400_get_regmap, .set_bias_level = wm8400_set_bias_level, .controls = wm8400_snd_controls, diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 3eb390be7dee..29f40a90fa79 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3999,8 +3999,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->hubs.codec = codec; - snd_soc_codec_set_cache_io(codec, control->regmap); - mutex_init(&wm8994->accdet_lock); INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap, wm1811_jackdet_bootstrap); @@ -4434,11 +4432,19 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) return 0; } +static struct regmap *wm8994_get_regmap(struct device *dev) +{ + struct wm8994 *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm8994 = { .probe = wm8994_codec_probe, .remove = wm8994_codec_remove, .suspend = wm8994_codec_suspend, .resume = wm8994_codec_resume, + .get_regmap = wm8994_get_regmap, .set_bias_level = wm8994_set_bias_level, }; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 004186b6bd48..09c4150840a3 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1051,11 +1051,6 @@ static struct snd_soc_dai_driver wm8997_dai[] = { static int wm8997_codec_probe(struct snd_soc_codec *codec) { struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); - int ret; - - ret = snd_soc_codec_set_cache_io(codec, priv->core.arizona->regmap); - if (ret != 0) - return ret; arizona_init_spk(codec); @@ -1086,9 +1081,17 @@ static unsigned int wm8997_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_5R, }; +static struct regmap *wm8997_get_regmap(struct device *dev) +{ + struct wm8997_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm8997 = { .probe = wm8997_codec_probe, .remove = wm8997_codec_remove, + .get_regmap = wm8997_get_regmap, .idle_bias_off = true, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7fa3b4a04bd3..520d2489d23c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -656,8 +656,8 @@ int snd_soc_suspend(struct device *dev) codec->driver->suspend(codec); codec->suspended = 1; codec->cache_sync = 1; - if (codec->using_regmap) - regcache_mark_dirty(codec->control_data); + if (codec->component.regmap) + regcache_mark_dirty(codec->component.regmap); /* deactivate pins to sleep state */ pinctrl_pm_select_sleep_state(codec->dev); break; @@ -847,14 +847,47 @@ EXPORT_SYMBOL_GPL(snd_soc_resume); static const struct snd_soc_dai_ops null_dai_ops = { }; +static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_node, + const char *codec_name) +{ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &codec_list, list) { + if (codec_of_node) { + if (codec->dev->of_node != codec_of_node) + continue; + } else { + if (strcmp(codec->name, codec_name)) + continue; + } + + return codec; + } + + return NULL; +} + +static struct snd_soc_dai *soc_find_codec_dai(struct snd_soc_codec *codec, + const char *codec_dai_name) +{ + struct snd_soc_dai *codec_dai; + + list_for_each_entry(codec_dai, &codec->component.dai_list, list) { + if (!strcmp(codec_dai->name, codec_dai_name)) { + return codec_dai; + } + } + + return NULL; +} + static int soc_bind_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_component *component; - struct snd_soc_codec *codec; struct snd_soc_platform *platform; - struct snd_soc_dai *codec_dai, *cpu_dai; + struct snd_soc_dai *cpu_dai; const char *platform_name; dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num); @@ -882,42 +915,24 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) return -EPROBE_DEFER; } - /* Find CODEC from registered CODECs */ - list_for_each_entry(codec, &codec_list, list) { - if (dai_link->codec_of_node) { - if (codec->dev->of_node != dai_link->codec_of_node) - continue; - } else { - if (strcmp(codec->name, dai_link->codec_name)) - continue; - } - - rtd->codec = codec; - - /* - * CODEC found, so find CODEC DAI from registered DAIs from - * this CODEC - */ - list_for_each_entry(codec_dai, &codec->component.dai_list, list) { - if (!strcmp(codec_dai->name, dai_link->codec_dai_name)) { - rtd->codec_dai = codec_dai; - break; - } - } - - if (!rtd->codec_dai) { - dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", - dai_link->codec_dai_name); - return -EPROBE_DEFER; - } - } - + /* Find CODEC from registered list */ + rtd->codec = soc_find_codec(dai_link->codec_of_node, + dai_link->codec_name); if (!rtd->codec) { dev_err(card->dev, "ASoC: CODEC %s not registered\n", dai_link->codec_name); return -EPROBE_DEFER; } + /* Find CODEC DAI from registered list */ + rtd->codec_dai = soc_find_codec_dai(rtd->codec, + dai_link->codec_dai_name); + if (!rtd->codec_dai) { + dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", + dai_link->codec_dai_name); + return -EPROBE_DEFER; + } + /* if there's no platform we match on the empty platform */ platform_name = dai_link->platform_name; if (!platform_name && !dai_link->platform_of_node) @@ -988,21 +1003,10 @@ static void soc_remove_codec(struct snd_soc_codec *codec) module_put(codec->dev->driver->owner); } -static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) +static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; int err; - /* unregister the rtd device */ - if (rtd->dev_registered) { - device_remove_file(rtd->dev, &dev_attr_pmdown_time); - device_remove_file(rtd->dev, &dev_attr_codec_reg); - device_unregister(rtd->dev); - rtd->dev_registered = 0; - } - - /* remove the CODEC DAI */ if (codec_dai && codec_dai->probed && codec_dai->driver->remove_order == order) { if (codec_dai->driver->remove) { @@ -1015,6 +1019,24 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) codec_dai->probed = 0; list_del(&codec_dai->card_list); } +} + +static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) +{ + struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; + int err; + + /* unregister the rtd device */ + if (rtd->dev_registered) { + device_remove_file(rtd->dev, &dev_attr_pmdown_time); + device_remove_file(rtd->dev, &dev_attr_codec_reg); + device_unregister(rtd->dev); + rtd->dev_registered = 0; + } + + /* remove the CODEC DAI */ + soc_remove_codec_dai(codec_dai, order); /* remove the cpu_dai */ if (cpu_dai && cpu_dai->probed && @@ -1145,16 +1167,6 @@ static int soc_probe_codec(struct snd_soc_card *card, codec->dapm.idle_bias_off = driver->idle_bias_off; - if (!codec->write && dev_get_regmap(codec->dev, NULL)) { - /* Set the default I/O up try regmap */ - ret = snd_soc_codec_set_cache_io(codec, NULL); - if (ret < 0) { - dev_err(codec->dev, - "Failed to set cache I/O: %d\n", ret); - goto err_probe; - } - } - if (driver->probe) { ret = driver->probe(codec); if (ret < 0) { @@ -1374,6 +1386,67 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, return 0; } +static int soc_probe_codec_dai(struct snd_soc_card *card, + struct snd_soc_dai *codec_dai, + int order) +{ + int ret; + + if (!codec_dai->probed && codec_dai->driver->probe_order == order) { + if (codec_dai->driver->probe) { + ret = codec_dai->driver->probe(codec_dai); + if (ret < 0) { + dev_err(codec_dai->dev, + "ASoC: failed to probe CODEC DAI %s: %d\n", + codec_dai->name, ret); + return ret; + } + } + + /* mark codec_dai as probed and add to card dai list */ + codec_dai->probed = 1; + list_add(&codec_dai->card_list, &card->dai_dev_list); + } + + return 0; +} + +static int soc_link_dai_widgets(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link, + struct snd_soc_dai *cpu_dai, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_dapm_widget *play_w, *capture_w; + int ret; + + /* link the DAI widgets */ + play_w = codec_dai->playback_widget; + capture_w = cpu_dai->capture_widget; + if (play_w && capture_w) { + ret = snd_soc_dapm_new_pcm(card, dai_link->params, + capture_w, play_w); + if (ret != 0) { + dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", + play_w->name, capture_w->name, ret); + return ret; + } + } + + play_w = cpu_dai->playback_widget; + capture_w = codec_dai->capture_widget; + if (play_w && capture_w) { + ret = snd_soc_dapm_new_pcm(card, dai_link->params, + capture_w, play_w); + if (ret != 0) { + dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", + play_w->name, capture_w->name, ret); + return ret; + } + } + + return 0; +} + static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; @@ -1382,7 +1455,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dapm_widget *play_w, *capture_w; int ret; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", @@ -1423,21 +1495,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } /* probe the CODEC DAI */ - if (!codec_dai->probed && codec_dai->driver->probe_order == order) { - if (codec_dai->driver->probe) { - ret = codec_dai->driver->probe(codec_dai); - if (ret < 0) { - dev_err(codec_dai->dev, - "ASoC: failed to probe CODEC DAI %s: %d\n", - codec_dai->name, ret); - return ret; - } - } - - /* mark codec_dai as probed and add to card dai list */ - codec_dai->probed = 1; - list_add(&codec_dai->card_list, &card->dai_dev_list); - } + ret = soc_probe_codec_dai(card, codec_dai, order); + if (ret) + return ret; /* complete DAI probe during last probe */ if (order != SND_SOC_COMP_ORDER_LAST) @@ -1475,29 +1535,10 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) codec2codec_close_delayed_work); /* link the DAI widgets */ - play_w = codec_dai->playback_widget; - capture_w = cpu_dai->capture_widget; - if (play_w && capture_w) { - ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); - if (ret != 0) { - dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", - play_w->name, capture_w->name, ret); - return ret; - } - } - - play_w = cpu_dai->playback_widget; - capture_w = codec_dai->capture_widget; - if (play_w && capture_w) { - ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); - if (ret != 0) { - dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", - play_w->name, capture_w->name, ret); - return ret; - } - } + ret = soc_link_dai_widgets(card, dai_link, + cpu_dai, codec_dai); + if (ret) + return ret; } } @@ -1509,14 +1550,15 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } #ifdef CONFIG_SND_SOC_AC97_BUS -static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) +static int soc_register_ac97_codec(struct snd_soc_codec *codec, + struct snd_soc_dai *codec_dai) { int ret; /* Only instantiate AC97 if not already done by the adaptor * for the generic AC97 subsystem. */ - if (rtd->codec_dai->driver->ac97_control && !rtd->codec->ac97_registered) { + if (codec_dai->driver->ac97_control && !codec->ac97_registered) { /* * It is possible that the AC97 device is already registered to * the device subsystem. This happens when the device is created @@ -1525,28 +1567,38 @@ static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) * * In those cases we don't try to register the device again. */ - if (!rtd->codec->ac97_created) + if (!codec->ac97_created) return 0; - ret = soc_ac97_dev_register(rtd->codec); + ret = soc_ac97_dev_register(codec); if (ret < 0) { - dev_err(rtd->codec->dev, + dev_err(codec->dev, "ASoC: AC97 device register failed: %d\n", ret); return ret; } - rtd->codec->ac97_registered = 1; + codec->ac97_registered = 1; } return 0; } -static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec) +static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) +{ + return soc_register_ac97_codec(rtd->codec, rtd->codec_dai); +} + +static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) { if (codec->ac97_registered) { soc_ac97_dev_unregister(codec); codec->ac97_registered = 0; } } + +static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) +{ + soc_unregister_ac97_codec(rtd->codec); +} #endif static int soc_check_aux_dev(struct snd_soc_card *card, int num) @@ -1845,7 +1897,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: failed to register AC97: %d\n", ret); while (--i >= 0) - soc_unregister_ac97_dai_link(card->rtd[i].codec); + soc_unregister_ac97_dai_link(&card->rtd[i]); goto probe_aux_dev_err; } } @@ -2195,7 +2247,7 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) { mutex_lock(&codec->mutex); #ifdef CONFIG_SND_SOC_AC97_BUS - soc_unregister_ac97_dai_link(codec); + soc_unregister_ac97_codec(codec); #endif kfree(codec->ac97->bus); kfree(codec->ac97); @@ -2301,7 +2353,7 @@ int snd_soc_add_codec_controls(struct snd_soc_codec *codec, struct snd_card *card = codec->card->snd_card; return snd_soc_add_controls(card, codec->dev, controls, num_controls, - codec->name_prefix, codec); + codec->name_prefix, &codec->component); } EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); @@ -2321,7 +2373,7 @@ int snd_soc_add_platform_controls(struct snd_soc_platform *platform, struct snd_card *card = platform->card->snd_card; return snd_soc_add_controls(card, platform->dev, controls, num_controls, - NULL, platform); + NULL, &platform->component); } EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); @@ -2405,12 +2457,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, item; unsigned int reg_val; + int ret; - reg_val = snd_soc_read(codec, e->reg); + ret = snd_soc_component_read(component, e->reg, ®_val); + if (ret) + return ret; val = (reg_val >> e->shift_l) & e->mask; item = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[0] = item; @@ -2436,7 +2491,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; unsigned int val; @@ -2453,38 +2508,48 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - return snd_soc_update_bits_locked(codec, e->reg, mask, val); + return snd_soc_component_update_bits(component, e->reg, mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); /** * snd_soc_read_signed - Read a codec register and interprete as signed value - * @codec: codec + * @component: component * @reg: Register to read * @mask: Mask to use after shifting the register value * @shift: Right shift of register value * @sign_bit: Bit that describes if a number is negative or not. + * @signed_val: Pointer to where the read value should be stored * * This functions reads a codec register. The register value is shifted right * by 'shift' bits and masked with the given 'mask'. Afterwards it translates * the given registervalue into a signed integer if sign_bit is non-zero. * - * Returns the register value as signed int. + * Returns 0 on sucess, otherwise an error value */ -static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg, - unsigned int mask, unsigned int shift, unsigned int sign_bit) +static int snd_soc_read_signed(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int shift, + unsigned int sign_bit, int *signed_val) { int ret; unsigned int val; - val = (snd_soc_read(codec, reg) >> shift) & mask; + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; + + val = (val >> shift) & mask; - if (!sign_bit) - return val; + if (!sign_bit) { + *signed_val = val; + return 0; + } /* non-negative number */ - if (!(val & BIT(sign_bit))) - return val; + if (!(val & BIT(sign_bit))) { + *signed_val = val; + return 0; + } ret = val; @@ -2496,7 +2561,9 @@ static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg, */ ret |= ~((int)(BIT(sign_bit) - 1)); - return ret; + *signed_val = ret; + + return 0; } /** @@ -2545,9 +2612,9 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw); int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -2557,25 +2624,32 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + int val; + int ret; if (sign_bit) mask = BIT(sign_bit + 1) - 1; - ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask, - shift, sign_bit) - min; + ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = val - min; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; if (snd_soc_volsw_is_stereo(mc)) { if (reg == reg2) - ucontrol->value.integer.value[1] = - snd_soc_read_signed(codec, reg, mask, rshift, - sign_bit) - min; + ret = snd_soc_read_signed(component, reg, mask, rshift, + sign_bit, &val); else - ucontrol->value.integer.value[1] = - snd_soc_read_signed(codec, reg2, mask, shift, - sign_bit) - min; + ret = snd_soc_read_signed(component, reg2, mask, shift, + sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = val - min; if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; @@ -2598,9 +2672,9 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw); int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -2635,12 +2709,13 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, type_2r = true; } } - err = snd_soc_update_bits_locked(codec, reg, val_mask, val); + err = snd_soc_component_update_bits(component, reg, val_mask, val); if (err < 0) return err; if (type_2r) - err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2); + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); return err; } @@ -2659,10 +2734,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -2670,13 +2744,23 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, int max = mc->max; int min = mc->min; int mask = (1 << (fls(min + max) - 1)) - 1; + unsigned int val; + int ret; - ucontrol->value.integer.value[0] = - ((snd_soc_read(codec, reg) >> shift) - min) & mask; + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; - if (snd_soc_volsw_is_stereo(mc)) - ucontrol->value.integer.value[1] = - ((snd_soc_read(codec, reg2) >> rshift) - min) & mask; + ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = snd_soc_component_read(component, reg2, &val); + if (ret < 0) + return ret; + + val = ((val >> rshift) - min) & mask; + ucontrol->value.integer.value[1] = val; + } return 0; } @@ -2694,7 +2778,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -2712,7 +2796,7 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0] + min) & mask; val = val << shift; - err = snd_soc_update_bits_locked(codec, reg, val_mask, val); + err = snd_soc_component_update_bits(component, reg, val_mask, val); if (err < 0) return err; @@ -2721,10 +2805,10 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, val2 = (ucontrol->value.integer.value[1] + min) & mask; val2 = val2 << rshift; - if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2)) - return err; + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); } - return 0; + return err; } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); @@ -2771,10 +2855,15 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; + unsigned int val; int min = mc->min; - int val = snd_soc_read(codec, reg); + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; ucontrol->value.integer.value[0] = ((signed char)(val & 0xff))-min; @@ -2798,7 +2887,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; int min = mc->min; unsigned int val; @@ -2806,7 +2895,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0]+min) & 0xff; val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; - return snd_soc_update_bits_locked(codec, reg, 0xffff, val); + return snd_soc_component_update_bits(component, reg, 0xffff, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); @@ -2855,7 +2944,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int rreg = mc->rreg; unsigned int shift = mc->shift; @@ -2872,7 +2961,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, val_mask = mask << shift; val = val << shift; - ret = snd_soc_update_bits_locked(codec, reg, val_mask, val); + ret = snd_soc_component_update_bits(component, reg, val_mask, val); if (ret < 0) return ret; @@ -2883,7 +2972,8 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, val_mask = mask << shift; val = val << shift; - ret = snd_soc_update_bits_locked(codec, rreg, val_mask, val); + ret = snd_soc_component_update_bits(component, rreg, val_mask, + val); } return ret; @@ -2902,9 +2992,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int rreg = mc->rreg; unsigned int shift = mc->shift; @@ -2912,9 +3002,14 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + unsigned int val; + int ret; - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2922,8 +3017,11 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] - min; if (snd_soc_volsw_is_stereo(mc)) { - ucontrol->value.integer.value[1] = - (snd_soc_read(codec, rreg) >> shift) & mask; + ret = snd_soc_component_read(component, rreg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; @@ -2977,11 +3075,11 @@ EXPORT_SYMBOL_GPL(snd_soc_limit_volume); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = params->num_regs * codec->val_bytes; + uinfo->count = params->num_regs * component->val_bytes; return 0; } @@ -2990,20 +3088,20 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_info); int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); int ret; - if (codec->using_regmap) - ret = regmap_raw_read(codec->control_data, params->base, + if (component->regmap) + ret = regmap_raw_read(component->regmap, params->base, ucontrol->value.bytes.data, - params->num_regs * codec->val_bytes); + params->num_regs * component->val_bytes); else ret = -EINVAL; /* Hide any masked bytes to ensure consistent data reporting */ if (ret == 0 && params->mask) { - switch (codec->val_bytes) { + switch (component->val_bytes) { case 1: ucontrol->value.bytes.data[0] &= ~params->mask; break; @@ -3027,16 +3125,16 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_get); int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); int ret, len; unsigned int val, mask; void *data; - if (!codec->using_regmap) + if (!component->regmap) return -EINVAL; - len = params->num_regs * codec->val_bytes; + len = params->num_regs * component->val_bytes; data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); if (!data) @@ -3048,27 +3146,27 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, * copy. */ if (params->mask) { - ret = regmap_read(codec->control_data, params->base, &val); + ret = regmap_read(component->regmap, params->base, &val); if (ret != 0) goto out; val &= params->mask; - switch (codec->val_bytes) { + switch (component->val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; ((u8 *)data)[0] |= val; break; case 2: mask = ~params->mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) goto out; ((u16 *)data)[0] &= mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) goto out; @@ -3077,14 +3175,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, break; case 4: mask = ~params->mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) goto out; ((u32 *)data)[0] &= mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) goto out; @@ -3097,7 +3195,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, } } - ret = regmap_raw_write(codec->control_data, params->base, + ret = regmap_raw_write(component->regmap, params->base, data, len); out: @@ -3160,24 +3258,27 @@ EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = codec->val_bytes * BITS_PER_BYTE; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; unsigned int regwmask = (1<<regwshift)-1; unsigned int invert = mc->invert; unsigned long mask = (1UL<<mc->nbits)-1; long min = mc->min; long max = mc->max; long val = 0; - unsigned long regval; + unsigned int regval; unsigned int i; + int ret; for (i = 0; i < regcount; i++) { - regval = snd_soc_read(codec, regbase+i) & regwmask; - val |= regval << (regwshift*(regcount-i-1)); + ret = snd_soc_component_read(component, regbase+i, ®val); + if (ret) + return ret; + val |= (regval & regwmask) << (regwshift*(regcount-i-1)); } val &= mask; if (min < 0 && val > max) @@ -3206,12 +3307,12 @@ EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = codec->val_bytes * BITS_PER_BYTE; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; unsigned int regwmask = (1<<regwshift)-1; unsigned int invert = mc->invert; unsigned long mask = (1UL<<mc->nbits)-1; @@ -3226,7 +3327,7 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, for (i = 0; i < regcount; i++) { regval = (val >> (regwshift*(regcount-i-1))) & regwmask; regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; - err = snd_soc_update_bits_locked(codec, regbase+i, + err = snd_soc_component_update_bits(component, regbase+i, regmask, regval); if (err < 0) return err; @@ -3248,14 +3349,21 @@ EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int mask = 1 << shift; unsigned int invert = mc->invert != 0; - unsigned int val = snd_soc_read(codec, reg) & mask; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + val &= mask; if (shift != 0 && val != 0) val = val >> shift; @@ -3278,9 +3386,9 @@ EXPORT_SYMBOL_GPL(snd_soc_get_strobe); int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int mask = 1 << shift; @@ -3290,12 +3398,11 @@ int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, unsigned int val2 = (strobe ^ invert) ? 0 : mask; int err; - err = snd_soc_update_bits_locked(codec, reg, mask, val1); + err = snd_soc_component_update_bits(component, reg, mask, val1); if (err < 0) return err; - err = snd_soc_update_bits_locked(codec, reg, mask, val2); - return err; + return snd_soc_component_update_bits(component, reg, mask, val2); } EXPORT_SYMBOL_GPL(snd_soc_put_strobe); @@ -3859,6 +3966,8 @@ __snd_soc_register_component(struct device *dev, return -ENOMEM; } + mutex_init(&cmpnt->io_mutex); + cmpnt->name = fmt_single_name(dev, &cmpnt->id); if (!cmpnt->name) { dev_err(dev, "ASoC: Failed to simplifying name\n"); @@ -3913,6 +4022,18 @@ int snd_soc_register_component(struct device *dev, } EXPORT_SYMBOL_GPL(snd_soc_register_component); +static void __snd_soc_unregister_component(struct snd_soc_component *cmpnt) +{ + snd_soc_unregister_dais(cmpnt); + + mutex_lock(&client_mutex); + list_del(&cmpnt->list); + mutex_unlock(&client_mutex); + + dev_dbg(cmpnt->dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); + kfree(cmpnt->name); +} + /** * snd_soc_unregister_component - Unregister a component from the ASoC core * @@ -3928,16 +4049,27 @@ void snd_soc_unregister_component(struct device *dev) return; found: - snd_soc_unregister_dais(cmpnt); + __snd_soc_unregister_component(cmpnt); +} +EXPORT_SYMBOL_GPL(snd_soc_unregister_component); - mutex_lock(&client_mutex); - list_del(&cmpnt->list); - mutex_unlock(&client_mutex); +static int snd_soc_platform_drv_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); - kfree(cmpnt->name); + return platform->driver->write(platform, reg, val); +} + +static int snd_soc_platform_drv_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + *val = platform->driver->read(platform, reg); + + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_unregister_component); /** * snd_soc_add_platform - Add a platform to the ASoC core @@ -3959,8 +4091,12 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->driver = platform_drv; platform->dapm.dev = dev; platform->dapm.platform = platform; + platform->dapm.component = &platform->component; platform->dapm.stream_event = platform_drv->stream_event; - mutex_init(&platform->mutex); + if (platform_drv->write) + platform->component.write = snd_soc_platform_drv_write; + if (platform_drv->read) + platform->component.read = snd_soc_platform_drv_read; /* register component */ ret = __snd_soc_register_component(dev, &platform->component, @@ -4013,7 +4149,7 @@ EXPORT_SYMBOL_GPL(snd_soc_register_platform); */ void snd_soc_remove_platform(struct snd_soc_platform *platform) { - snd_soc_unregister_component(platform->dev); + __snd_soc_unregister_component(&platform->component); mutex_lock(&client_mutex); list_del(&platform->list); @@ -4089,6 +4225,24 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) stream->formats |= codec_format_map[i]; } +static int snd_soc_codec_drv_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + return codec->driver->write(codec, reg, val); +} + +static int snd_soc_codec_drv_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + *val = codec->driver->read(codec, reg); + + return 0; +} + /** * snd_soc_register_codec - Register a codec with the ASoC core * @@ -4100,6 +4254,7 @@ int snd_soc_register_codec(struct device *dev, int num_dai) { struct snd_soc_codec *codec; + struct regmap *regmap; int ret, i; dev_dbg(dev, "codec register %s\n", dev_name(dev)); @@ -4115,20 +4270,41 @@ int snd_soc_register_codec(struct device *dev, goto fail_codec; } - codec->write = codec_drv->write; - codec->read = codec_drv->read; + if (codec_drv->write) + codec->component.write = snd_soc_codec_drv_write; + if (codec_drv->read) + codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; + codec->dapm.component = &codec->component; codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dapm.stream_event = codec_drv->stream_event; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; - codec->val_bytes = codec_drv->reg_word_size; + codec->component.val_bytes = codec_drv->reg_word_size; mutex_init(&codec->mutex); + if (!codec->component.write) { + if (codec_drv->get_regmap) + regmap = codec_drv->get_regmap(dev); + else + regmap = dev_get_regmap(dev, NULL); + + if (regmap) { + ret = snd_soc_component_init_io(&codec->component, + regmap); + if (ret) { + dev_err(codec->dev, + "Failed to set cache I/O:%d\n", + ret); + return ret; + } + } + } + for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); @@ -4178,7 +4354,7 @@ void snd_soc_unregister_codec(struct device *dev) return; found: - snd_soc_unregister_component(dev); + __snd_soc_unregister_component(&codec->component); mutex_lock(&client_mutex); list_del(&codec->list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c8a780d0d057..98c1dc6e1152 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -379,86 +379,24 @@ static void dapm_reset(struct snd_soc_card *card) static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg, unsigned int *value) { - if (w->codec) { - *value = snd_soc_read(w->codec, reg); - return 0; - } else if (w->platform) { - *value = snd_soc_platform_read(w->platform, reg); - return 0; - } - - dev_err(w->dapm->dev, "ASoC: no valid widget read method\n"); - return -1; -} - -static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, - unsigned int val) -{ - if (w->codec) - return snd_soc_write(w->codec, reg, val); - else if (w->platform) - return snd_soc_platform_write(w->platform, reg, val); - - dev_err(w->dapm->dev, "ASoC: no valid widget write method\n"); - return -1; -} - -static inline void soc_widget_lock(struct snd_soc_dapm_widget *w) -{ - if (w->codec && !w->codec->using_regmap) - mutex_lock(&w->codec->mutex); - else if (w->platform) - mutex_lock(&w->platform->mutex); + if (!w->dapm->component) + return -EIO; + return snd_soc_component_read(w->dapm->component, reg, value); } -static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w) +static int soc_widget_update_bits(struct snd_soc_dapm_widget *w, + int reg, unsigned int mask, unsigned int value) { - if (w->codec && !w->codec->using_regmap) - mutex_unlock(&w->codec->mutex); - else if (w->platform) - mutex_unlock(&w->platform->mutex); + if (!w->dapm->component) + return -EIO; + return snd_soc_component_update_bits_async(w->dapm->component, reg, + mask, value); } static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) { - if (dapm->codec && dapm->codec->using_regmap) - regmap_async_complete(dapm->codec->control_data); -} - -static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, - unsigned short reg, unsigned int mask, unsigned int value) -{ - bool change; - unsigned int old, new; - int ret; - - if (w->codec && w->codec->using_regmap) { - ret = regmap_update_bits_check_async(w->codec->control_data, - reg, mask, value, - &change); - if (ret != 0) - return ret; - } else { - soc_widget_lock(w); - ret = soc_widget_read(w, reg, &old); - if (ret < 0) { - soc_widget_unlock(w); - return ret; - } - - new = (old & ~mask) | (value & mask); - change = old != new; - if (change) { - ret = soc_widget_write(w, reg, new); - if (ret < 0) { - soc_widget_unlock(w); - return ret; - } - } - soc_widget_unlock(w); - } - - return change; + if (dapm->component) + snd_soc_component_async_complete(dapm->component); } /** @@ -1133,7 +1071,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, else val = w->off_val; - soc_widget_update_bits_locked(w, -(w->reg + 1), + soc_widget_update_bits(w, -(w->reg + 1), w->mask << w->shift, val << w->shift); return 0; @@ -1429,7 +1367,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); - soc_widget_update_bits_locked(w, reg, mask, value); + soc_widget_update_bits(w, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -1575,8 +1513,7 @@ static void dapm_widget_update(struct snd_soc_card *card) if (!w) return; - ret = soc_widget_update_bits_locked(w, update->reg, update->mask, - update->val); + ret = soc_widget_update_bits(w, update->reg, update->mask, update->val); if (ret < 0) dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 5fb85783b044..7767fbd73eb7 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -17,93 +17,220 @@ #include <linux/export.h> #include <sound/soc.h> -#include <trace/events/asoc.h> +/** + * snd_soc_component_read() - Read register value + * @component: Component to read from + * @reg: Register to read + * @val: Pointer to where the read value is stored + * + * Return: 0 on success, a negative error code otherwise. + */ +int snd_soc_component_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) +{ + int ret; -unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) + if (component->regmap) + ret = regmap_read(component->regmap, reg, val); + else if (component->read) + ret = component->read(component, reg, val); + else + ret = -EIO; + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_read); + +/** + * snd_soc_component_write() - Write register value + * @component: Component to write to + * @reg: Register to write + * @val: Value to write to the register + * + * Return: 0 on success, a negative error code otherwise. + */ +int snd_soc_component_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) { - unsigned int ret; + if (component->regmap) + return regmap_write(component->regmap, reg, val); + else if (component->write) + return component->write(component, reg, val); + else + return -EIO; +} +EXPORT_SYMBOL_GPL(snd_soc_component_write); - ret = codec->read(codec, reg); - dev_dbg(codec->dev, "read %x => %x\n", reg, ret); - trace_snd_soc_reg_read(codec, reg, ret); +static int snd_soc_component_update_bits_legacy( + struct snd_soc_component *component, unsigned int reg, + unsigned int mask, unsigned int val, bool *change) +{ + unsigned int old, new; + int ret; + + if (!component->read || !component->write) + return -EIO; + + mutex_lock(&component->io_mutex); + + ret = component->read(component, reg, &old); + if (ret < 0) + goto out_unlock; + + new = (old & ~mask) | (val & mask); + *change = old != new; + if (*change) + ret = component->write(component, reg, new); +out_unlock: + mutex_unlock(&component->io_mutex); return ret; } -EXPORT_SYMBOL_GPL(snd_soc_read); -int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) +/** + * snd_soc_component_update_bits() - Perform read/modify/write cycle + * @component: Component to update + * @reg: Register to update + * @mask: Mask that specifies which bits to update + * @val: New value for the bits specified by mask + * + * Return: 1 if the operation was successful and the value of the register + * changed, 0 if the operation was successful, but the value did not change. + * Returns a negative error code otherwise. + */ +int snd_soc_component_update_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val) { - dev_dbg(codec->dev, "write %x = %x\n", reg, val); - trace_snd_soc_reg_write(codec, reg, val); - return codec->write(codec, reg, val); + bool change; + int ret; + + if (component->regmap) + ret = regmap_update_bits_check(component->regmap, reg, mask, + val, &change); + else + ret = snd_soc_component_update_bits_legacy(component, reg, + mask, val, &change); + + if (ret < 0) + return ret; + return change; } -EXPORT_SYMBOL_GPL(snd_soc_write); +EXPORT_SYMBOL_GPL(snd_soc_component_update_bits); /** - * snd_soc_update_bits - update codec register bits - * @codec: audio codec - * @reg: codec register - * @mask: register mask - * @value: new value + * snd_soc_component_update_bits_async() - Perform asynchronous + * read/modify/write cycle + * @component: Component to update + * @reg: Register to update + * @mask: Mask that specifies which bits to update + * @val: New value for the bits specified by mask * - * Writes new register value. + * This function is similar to snd_soc_component_update_bits(), but the update + * operation is scheduled asynchronously. This means it may not be completed + * when the function returns. To make sure that all scheduled updates have been + * completed snd_soc_component_async_complete() must be called. * - * Returns 1 for change, 0 for no change, or negative error code. + * Return: 1 if the operation was successful and the value of the register + * changed, 0 if the operation was successful, but the value did not change. + * Returns a negative error code otherwise. */ -int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, - unsigned int mask, unsigned int value) +int snd_soc_component_update_bits_async(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val) { bool change; - unsigned int old, new; int ret; - if (codec->using_regmap) { - ret = regmap_update_bits_check(codec->control_data, reg, - mask, value, &change); - } else { - ret = snd_soc_read(codec, reg); - if (ret < 0) - return ret; - - old = ret; - new = (old & ~mask) | (value & mask); - change = old != new; - if (change) - ret = snd_soc_write(codec, reg, new); - } + if (component->regmap) + ret = regmap_update_bits_check_async(component->regmap, reg, + mask, val, &change); + else + ret = snd_soc_component_update_bits_legacy(component, reg, + mask, val, &change); if (ret < 0) return ret; - return change; } -EXPORT_SYMBOL_GPL(snd_soc_update_bits); +EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); + +/** + * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed + * @component: Component for which to wait + * + * This function blocks until all asynchronous I/O which has previously been + * scheduled using snd_soc_component_update_bits_async() has completed. + */ +void snd_soc_component_async_complete(struct snd_soc_component *component) +{ + if (component->regmap) + regmap_async_complete(component->regmap); +} +EXPORT_SYMBOL_GPL(snd_soc_component_async_complete); + +/** + * snd_soc_component_test_bits - Test register for change + * @component: component + * @reg: Register to test + * @mask: Mask that specifies which bits to test + * @value: Value to test against + * + * Tests a register with a new value and checks if the new value is + * different from the old value. + * + * Return: 1 for change, otherwise 0. + */ +int snd_soc_component_test_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int value) +{ + unsigned int old, new; + int ret; + + ret = snd_soc_component_read(component, reg, &old); + if (ret < 0) + return ret; + new = (old & ~mask) | value; + return old != new; +} +EXPORT_SYMBOL_GPL(snd_soc_component_test_bits); + +unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_component_read(&codec->component, reg, &val); + if (ret < 0) + return -1; + + return val; +} +EXPORT_SYMBOL_GPL(snd_soc_read); + +int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + return snd_soc_component_write(&codec->component, reg, val); +} +EXPORT_SYMBOL_GPL(snd_soc_write); /** - * snd_soc_update_bits_locked - update codec register bits + * snd_soc_update_bits - update codec register bits * @codec: audio codec * @reg: codec register * @mask: register mask * @value: new value * - * Writes new register value, and takes the codec mutex. + * Writes new register value. * - * Returns 1 for change else 0. + * Returns 1 for change, 0 for no change, or negative error code. */ -int snd_soc_update_bits_locked(struct snd_soc_codec *codec, - unsigned int reg, unsigned int mask, - unsigned int value) +int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, + unsigned int mask, unsigned int value) { - int change; - - mutex_lock(&codec->mutex); - change = snd_soc_update_bits(codec, reg, mask, value); - mutex_unlock(&codec->mutex); - - return change; + return snd_soc_component_update_bits(&codec->component, reg, mask, + value); } -EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked); +EXPORT_SYMBOL_GPL(snd_soc_update_bits); /** * snd_soc_test_bits - test register for change @@ -120,118 +247,55 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked); int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value) { - int change; - unsigned int old, new; - - old = snd_soc_read(codec, reg); - new = (old & ~mask) | value; - change = old != new; - - return change; + return snd_soc_component_test_bits(&codec->component, reg, mask, value); } EXPORT_SYMBOL_GPL(snd_soc_test_bits); int snd_soc_platform_read(struct snd_soc_platform *platform, unsigned int reg) { - unsigned int ret; + unsigned int val; + int ret; - if (!platform->driver->read) { - dev_err(platform->dev, "ASoC: platform has no read back\n"); + ret = snd_soc_component_read(&platform->component, reg, &val); + if (ret < 0) return -1; - } - ret = platform->driver->read(platform, reg); - dev_dbg(platform->dev, "read %x => %x\n", reg, ret); - trace_snd_soc_preg_read(platform, reg, ret); - - return ret; + return val; } EXPORT_SYMBOL_GPL(snd_soc_platform_read); int snd_soc_platform_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val) { - if (!platform->driver->write) { - dev_err(platform->dev, "ASoC: platform has no write back\n"); - return -1; - } - - dev_dbg(platform->dev, "write %x = %x\n", reg, val); - trace_snd_soc_preg_write(platform, reg, val); - return platform->driver->write(platform, reg, val); + return snd_soc_component_write(&platform->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_platform_write); -#ifdef CONFIG_REGMAP -static int hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - return regmap_write(codec->control_data, reg, value); -} - -static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) -{ - int ret; - unsigned int val; - - ret = regmap_read(codec->control_data, reg, &val); - if (ret == 0) - return val; - else - return -1; -} - /** - * snd_soc_codec_set_cache_io: Set up standard I/O functions. - * - * @codec: CODEC to configure. - * @map: Register map to write to + * snd_soc_component_init_io() - Initialize regmap IO * - * Register formats are frequently shared between many I2C and SPI - * devices. In order to promote code reuse the ASoC core provides - * some standard implementations of CODEC read and write operations - * which can be set up using this function. + * @component: component to initialize + * @regmap: regmap instance to use for IO operations * - * The caller is responsible for allocating and initialising the - * actual cache. - * - * Note that at present this code cannot be used by CODECs with - * volatile registers. + * Return: 0 on success, a negative error code otherwise */ -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - struct regmap *regmap) +int snd_soc_component_init_io(struct snd_soc_component *component, + struct regmap *regmap) { int ret; - /* Device has made its own regmap arrangements */ if (!regmap) - codec->control_data = dev_get_regmap(codec->dev, NULL); - else - codec->control_data = regmap; + return -EINVAL; - if (IS_ERR(codec->control_data)) - return PTR_ERR(codec->control_data); - - codec->write = hw_write; - codec->read = hw_read; - - ret = regmap_get_val_bytes(codec->control_data); + ret = regmap_get_val_bytes(regmap); /* Errors are legitimate for non-integer byte * multiples */ if (ret > 0) - codec->val_bytes = ret; + component->val_bytes = ret; - codec->using_regmap = true; + component->regmap = regmap; return 0; } -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); -#else -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - struct regmap *regmap) -{ - return -ENOTSUPP; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); -#endif +EXPORT_SYMBOL_GPL(snd_soc_component_init_io); |