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 00000000000..950188891ab --- /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 ef78f562f4a..75020f52acd 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 3da5f2328ff..e84f22f8976 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 00000000000..3a3298106b2 --- /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 03996b2bb04..c75c795a377 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 b18cafa5a85..3c4b10ff48c 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 f0e84013788..c7b853f520c 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 3c4d275d064..efdb4d06020 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 1e25c7af853..5ee48c8e484 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 2c59b1fb69d..9029e20514e 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 244c097cd90..f26befb0c29 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 00000000000..12ebbaf5d95 --- /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; |