diff options
368 files changed, 17063 insertions, 4617 deletions
diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl index 71f9246..e94a10b 100644 --- a/Documentation/DocBook/alsa-driver-api.tmpl +++ b/Documentation/DocBook/alsa-driver-api.tmpl @@ -108,7 +108,7 @@ <sect1><title>ASoC Core API</title> !Iinclude/sound/soc.h !Esound/soc/soc-core.c -!Esound/soc/soc-cache.c +<!-- !Esound/soc/soc-cache.c no docbook comments here --> !Esound/soc/soc-devres.c !Esound/soc/soc-io.c !Esound/soc/soc-pcm.c diff --git a/Documentation/devicetree/bindings/sound/cs4349.txt b/Documentation/devicetree/bindings/sound/cs4349.txt new file mode 100644 index 0000000..54c117b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs4349.txt @@ -0,0 +1,19 @@ +CS4349 audio CODEC + +Required properties: + + - compatible : "cirrus,cs4349" + + - reg : the I2C address of the device for I2C + +Optional properties: + + - reset-gpios : a GPIO spec for the reset pin. + +Example: + +codec: cs4349@48 { + compatible = "cirrus,cs4349"; + reg = <0x48>; + reset-gpios = <&gpio 54 0>; +}; diff --git a/Documentation/devicetree/bindings/sound/ics43432.txt b/Documentation/devicetree/bindings/sound/ics43432.txt new file mode 100644 index 0000000..b02e3a6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ics43432.txt @@ -0,0 +1,17 @@ +Invensense ICS-43432 MEMS microphone with I2S output. + +There are no software configuration options for this device, indeed, the only +host connection is the I2S interface. Apart from requirements on clock +frequency (460 kHz to 3.379 MHz according to the data sheet) there must be +64 clock cycles in each stereo output frame; 24 of the 32 available bits +contain audio data. A hardware pin determines if the device outputs data +on the left or right channel of the I2S frame. + +Required properties: + - compatible : Must be "invensense,ics43432" + +Example: + + ics43432: ics43432 { + compatible = "invensense,ics43432"; + }; diff --git a/Documentation/devicetree/bindings/sound/max98357a.txt b/Documentation/devicetree/bindings/sound/max98357a.txt index a7a149a..28645a2 100644 --- a/Documentation/devicetree/bindings/sound/max98357a.txt +++ b/Documentation/devicetree/bindings/sound/max98357a.txt @@ -4,7 +4,11 @@ This node models the Maxim MAX98357A DAC. Required properties: - compatible : "maxim,max98357a" -- sdmode-gpios : GPIO specifier for the GPIO -> DAC SDMODE pin + +Optional properties: +- sdmode-gpios : GPIO specifier for the chip's SD_MODE pin. + If this option is not specified then driver does not manage + the pin state (e.g. chip is always on). Example: diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index b6b3a78..1173395 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -18,6 +18,12 @@ Required properties: - rcar_sound,src : Should contain SRC feature. The number of SRC subnode should be same as HW. see below for detail. +- rcar_sound,ctu : Should contain CTU feature. + The number of CTU subnode should be same as HW. + see below for detail. +- rcar_sound,mix : Should contain MIX feature. + The number of MIX subnode should be same as HW. + see below for detail. - rcar_sound,dvc : Should contain DVC feature. The number of DVC subnode should be same as HW. see below for detail. @@ -90,6 +96,22 @@ rcar_sound: sound@ec500000 { }; }; + rcar_sound,mix { + mix0: mix@0 { }; + mix1: mix@1 { }; + }; + + rcar_sound,ctu { + ctu00: ctu@0 { }; + ctu01: ctu@1 { }; + ctu02: ctu@2 { }; + ctu03: ctu@3 { }; + ctu10: ctu@4 { }; + ctu11: ctu@5 { }; + ctu12: ctu@6 { }; + ctu13: ctu@7 { }; + }; + rcar_sound,src { src0: src@0 { interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt index c641550..962748a 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt @@ -6,6 +6,7 @@ Required properties: - compatible : "renesas,rsrc-card,<board>" Examples with soctypes are: + - "renesas,rsrc-card" - "renesas,rsrc-card,lager" - "renesas,rsrc-card,koelsch" Optional properties: @@ -29,6 +30,12 @@ Optional subnode properties: - frame-inversion : bool property. Add this if the dai-link uses frame clock inversion. - convert-rate : platform specified sampling rate convert +- audio-prefix : see audio-routing +- audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources. + use audio-prefix if some components is using same sink/sources naming. + it can be used if compatible was "renesas,rsrc-card"; Required CPU/CODEC subnodes properties: diff --git a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt new file mode 100644 index 0000000..a805aa9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt @@ -0,0 +1,19 @@ +ROCKCHIP with MAX98090 CODEC + +Required properties: +- compatible: "rockchip,rockchip-audio-max98090" +- rockchip,model: The user-visible name of this sound complex +- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's + connected to the CODEC +- rockchip,audio-codec: The phandle of the MAX98090 audio codec +- rockchip,headset-codec: The phandle of Ext chip for jack detection + +Example: + +sound { + compatible = "rockchip,rockchip-audio-max98090"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,audio-codec = <&max98090>; + rockchip,headset-codec = <&headsetcodec>; +}; diff --git a/Documentation/devicetree/bindings/sound/rockchip-rt5645.txt b/Documentation/devicetree/bindings/sound/rockchip-rt5645.txt new file mode 100644 index 0000000..411a62b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip-rt5645.txt @@ -0,0 +1,17 @@ +ROCKCHIP with RT5645/RT5650 CODECS + +Required properties: +- compatible: "rockchip,rockchip-audio-rt5645" +- rockchip,model: The user-visible name of this sound complex +- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's + connected to the CODEC +- rockchip,audio-codec: The phandle of the RT5645/RT5650 audio codec + +Example: + +sound { + compatible = "rockchip,rockchip-audio-rt5645"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,audio-codec = <&rt5645>; +}; diff --git a/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt b/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt new file mode 100644 index 0000000..028fa1c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt @@ -0,0 +1,155 @@ +STMicroelectronics sti ASoC cards + +The sti ASoC Sound Card can be used, for all sti SoCs using internal sti-sas +codec or external codecs. + +sti sound drivers allows to expose sti SoC audio interface through the +generic ASoC simple card. For details about sound card declaration please refer to +Documentation/devicetree/bindings/sound/simple-card.txt. + +1) sti-uniperiph-dai: audio dai device. +--------------------------------------- + +Required properties: + - compatible: "st,sti-uni-player" or "st,sti-uni-reader" + + - st,syscfg: phandle to boot-device system configuration registers + + - clock-names: name of the clocks listed in clocks property in the same order + + - reg: CPU DAI IP Base address and size entries, listed in same + order than the CPU_DAI properties. + + - reg-names: names of the mapped memory regions listed in regs property in + the same order. + + - interrupts: CPU_DAI interrupt line, listed in the same order than the + CPU_DAI properties. + + - dma: CPU_DAI DMA controller phandle and DMA request line, listed in the same + order than the CPU_DAI properties. + + - dma-names: identifier string for each DMA request line in the dmas property. + "tx" for "st,sti-uni-player" compatibility + "rx" for "st,sti-uni-reader" compatibility + + - version: IP version integrated in SOC. + + - dai-name: DAI name that describes the IP. + +Required properties ("st,sti-uni-player" compatibility only): + - clocks: CPU_DAI IP clock source, listed in the same order than the + CPU_DAI properties. + + - uniperiph-id: internal SOC IP instance ID. + + - IP mode: IP working mode depending on associated codec. + "HDMI" connected to HDMI codec IP and IEC HDMI formats. + "SPDIF"connected to SPDIF codec and support SPDIF formats. + "PCM" PCM standard mode for I2S or TDM bus. + +Optional properties: + - pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for + external codecs connection. + + - pinctrl-names: should contain only one value - "default". + +Example: + + sti_uni_player2: sti-uni-player@2 { + compatible = "st,sti-uni-player"; + status = "okay"; + #sound-dai-cells = <0>; + st,syscfg = <&syscfg_core>; + clocks = <&clk_s_d0_flexgen CLK_PCM_2>; + reg = <0x8D82000 0x158>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + dmas = <&fdma0 4 0 1>; + dai-name = "Uni Player #1 (DAC)"; + dma-names = "tx"; + uniperiph-id = <2>; + version = <5>; + mode = "PCM"; + }; + + sti_uni_player3: sti-uni-player@3 { + compatible = "st,sti-uni-player"; + status = "okay"; + #sound-dai-cells = <0>; + st,syscfg = <&syscfg_core>; + clocks = <&clk_s_d0_flexgen CLK_SPDIFF>; + reg = <0x8D85000 0x158>; + interrupts = <GIC_SPI 89 IRQ_TYPE_NONE>; + dmas = <&fdma0 7 0 1>; + dma-names = "tx"; + dai-name = "Uni Player #1 (PIO)"; + uniperiph-id = <3>; + version = <5>; + mode = "SPDIF"; + }; + + sti_uni_reader1: sti-uni-reader@1 { + compatible = "st,sti-uni-reader"; + status = "disabled"; + #sound-dai-cells = <0>; + st,syscfg = <&syscfg_core>; + reg = <0x8D84000 0x158>; + interrupts = <GIC_SPI 88 IRQ_TYPE_NONE>; + dmas = <&fdma0 6 0 1>; + dma-names = "rx"; + dai-name = "Uni Reader #1 (HDMI RX)"; + version = <3>; + }; + +2) sti-sas-codec: internal audio codec IPs driver +------------------------------------------------- + +Required properties: + - compatible: "st,sti<chip>-sas-codec" . + Should be chip "st,stih416-sas-codec" or "st,stih407-sas-codec" + + - st,syscfg: phandle to boot-device system configuration registers. + + - pinctrl-0: SPDIF PIO description. + + - pinctrl-names: should contain only one value - "default". + +Example: + sti_sas_codec: sti-sas-codec { + compatible = "st,stih407-sas-codec"; + #sound-dai-cells = <1>; + st,reg_audio = <&syscfg_core>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_out >; + }; + +Example of audio card declaration: + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "sti audio card"; + status = "okay"; + + simple-audio-card,dai-link@0 { + /* DAC */ + format = "i2s"; + dai-tdm-slot-width = <32>; + cpu { + sound-dai = <&sti_uni_player2>; + }; + + codec { + sound-dai = <&sti_sasg_codec 1>; + }; + }; + simple-audio-card,dai-link@1 { + /* SPDIF */ + format = "left_j"; + cpu { + sound-dai = <&sti_uni_player3>; + }; + + codec { + sound-dai = <&sti_sasg_codec 0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 80a7209..2a85b15 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -111,6 +111,7 @@ ingenic Ingenic Semiconductor innolux Innolux Corporation intel Intel Corporation intercontrol Inter Control Group +invensense InvenSense Inc. isee ISEE 2007 S.L. isil Intersil jedec JEDEC Solid State Technology Association @@ -155,6 +156,7 @@ nvidia NVIDIA nxp NXP Semiconductors onnn ON Semiconductor Corp. opencores OpenCores.org +option Option NV ortustech Ortus Technology Co., Ltd. ovti OmniVision Technologies panasonic Panasonic Corporation diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b2b2849..873ddf9 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -136,7 +136,7 @@ struct regmap { /* if set, the HW registers are known to match map->reg_defaults */ bool no_sync_defaults; - struct reg_default *patch; + struct reg_sequence *patch; int patch_regs; /* if set, converts bulk rw to single rw */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04..0a849ee 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -34,7 +34,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, - bool *change); + bool *change, bool force_write); static int _regmap_bus_reg_read(void *context, unsigned int reg, unsigned int *val); @@ -1178,7 +1178,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, ret = _regmap_update_bits(map, range->selector_reg, range->selector_mask, win_page << range->selector_shift, - &page_chg); + &page_chg, false); map->work_buf = orig_work_buf; @@ -1624,6 +1624,18 @@ int regmap_fields_write(struct regmap_field *field, unsigned int id, } EXPORT_SYMBOL_GPL(regmap_fields_write); +int regmap_fields_force_write(struct regmap_field *field, unsigned int id, + unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + return regmap_write_bits(field->regmap, + field->reg + (field->id_offset * id), + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_force_write); + /** * regmap_fields_update_bits(): Perform a read/modify/write cycle * on the register field @@ -1743,7 +1755,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * relative. The page register has been written if that was neccessary. */ static int _regmap_raw_multi_reg_write(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, size_t num_regs) { int ret; @@ -1800,12 +1812,12 @@ static unsigned int _regmap_register_page(struct regmap *map, } static int _regmap_range_multi_paged_reg_write(struct regmap *map, - struct reg_default *regs, + struct reg_sequence *regs, size_t num_regs) { int ret; int i, n; - struct reg_default *base; + struct reg_sequence *base; unsigned int this_page = 0; /* * the set of registers are not neccessarily in order, but @@ -1843,7 +1855,7 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, } static int _regmap_multi_reg_write(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, size_t num_regs) { int i; @@ -1895,8 +1907,8 @@ static int _regmap_multi_reg_write(struct regmap *map, struct regmap_range_node *range; range = _regmap_range_lookup(map, reg); if (range) { - size_t len = sizeof(struct reg_default)*num_regs; - struct reg_default *base = kmemdup(regs, len, + size_t len = sizeof(struct reg_sequence)*num_regs; + struct reg_sequence *base = kmemdup(regs, len, GFP_KERNEL); if (!base) return -ENOMEM; @@ -1929,7 +1941,7 @@ static int _regmap_multi_reg_write(struct regmap *map, * A value of zero will be returned on success, a negative errno will be * returned in error cases. */ -int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, +int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs) { int ret; @@ -1962,7 +1974,7 @@ EXPORT_SYMBOL_GPL(regmap_multi_reg_write); * be returned in error cases. */ int regmap_multi_reg_write_bypassed(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs) { int ret; @@ -2327,7 +2339,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_read); static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, - bool *change) + bool *change, bool force_write) { int ret; unsigned int tmp, orig; @@ -2339,7 +2351,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, tmp = orig & ~mask; tmp |= val & mask; - if (tmp != orig) { + if (force_write || (tmp != orig)) { ret = _regmap_write(map, reg, tmp); if (change) *change = true; @@ -2367,7 +2379,7 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, int ret; map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, NULL); + ret = _regmap_update_bits(map, reg, mask, val, NULL, false); map->unlock(map->lock_arg); return ret; @@ -2375,6 +2387,29 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, EXPORT_SYMBOL_GPL(regmap_update_bits); /** + * regmap_write_bits: Perform a read/modify/write cycle on the register map + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * + * Returns zero for success, a negative number on error. + */ +int regmap_write_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + + map->lock(map->lock_arg); + ret = _regmap_update_bits(map, reg, mask, val, NULL, true); + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_write_bits); + +/** * regmap_update_bits_async: Perform a read/modify/write cycle on the register * map asynchronously * @@ -2398,7 +2433,7 @@ int regmap_update_bits_async(struct regmap *map, unsigned int reg, map->async = true; - ret = _regmap_update_bits(map, reg, mask, val, NULL); + ret = _regmap_update_bits(map, reg, mask, val, NULL, false); map->async = false; @@ -2427,7 +2462,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, int ret; map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, change); + ret = _regmap_update_bits(map, reg, mask, val, change, false); map->unlock(map->lock_arg); return ret; } @@ -2460,7 +2495,7 @@ int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, map->async = true; - ret = _regmap_update_bits(map, reg, mask, val, change); + ret = _regmap_update_bits(map, reg, mask, val, change, false); map->async = false; @@ -2552,10 +2587,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); * The caller must ensure that this function cannot be called * concurrently with either itself or regcache_sync(). */ -int regmap_register_patch(struct regmap *map, const struct reg_default *regs, +int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs, int num_regs) { - struct reg_default *p; + struct reg_sequence *p; int ret; bool bypass; @@ -2564,7 +2599,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, return 0; p = krealloc(map->patch, - sizeof(struct reg_default) * (map->patch_regs + num_regs), + sizeof(struct reg_sequence) * (map->patch_regs + num_regs), GFP_KERNEL); if (p) { memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs)); diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 2aaa3c8..00416f2 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -54,7 +54,7 @@ static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) } /* ADI recommended values for proper operation. */ -static const struct reg_default adv7511_fixed_registers[] = { +static const struct reg_sequence adv7511_fixed_registers[] = { { 0x98, 0x03 }, { 0x9a, 0xe0 }, { 0x9c, 0x30 }, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index fd1de45..1fc327d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1809,6 +1809,7 @@ struct drm_i915_private { struct drm_property *force_audio_property; /* hda/i915 audio component */ + struct i915_audio_component *audio_component; bool audio_component_registered; uint32_t hw_context_size; diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 3da9b84..678a34f 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -399,6 +399,9 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder) struct drm_connector *connector; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_audio_component *acomp = dev_priv->audio_component; + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + enum port port = intel_dig_port->port; connector = drm_select_eld(encoder, mode); if (!connector) @@ -419,6 +422,9 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder) if (dev_priv->display.audio_codec_enable) dev_priv->display.audio_codec_enable(connector, intel_encoder, mode); + + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port); } /** @@ -428,13 +434,20 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder) * The disable sequences must be performed before disabling the transcoder or * port. */ -void intel_audio_codec_disable(struct intel_encoder *encoder) +void intel_audio_codec_disable(struct intel_encoder *intel_encoder) { - struct drm_device *dev = encoder->base.dev; + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_audio_component *acomp = dev_priv->audio_component; + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + enum port port = intel_dig_port->port; if (dev_priv->display.audio_codec_disable) - dev_priv->display.audio_codec_disable(encoder); + dev_priv->display.audio_codec_disable(intel_encoder); + + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port); } /** @@ -525,12 +538,16 @@ static int i915_audio_component_bind(struct device *i915_dev, struct device *hda_dev, void *data) { struct i915_audio_component *acomp = data; + struct drm_i915_private *dev_priv = dev_to_i915(i915_dev); if (WARN_ON(acomp->ops || acomp->dev)) return -EEXIST; + drm_modeset_lock_all(dev_priv->dev); acomp->ops = &i915_audio_component_ops; acomp->dev = i915_dev; + dev_priv->audio_component = acomp; + drm_modeset_unlock_all(dev_priv->dev); return 0; } @@ -539,9 +556,13 @@ static void i915_audio_component_unbind(struct device *i915_dev, struct device *hda_dev, void *data) { struct i915_audio_component *acomp = data; + struct drm_i915_private *dev_priv = dev_to_i915(i915_dev); + drm_modeset_lock_all(dev_priv->dev); acomp->ops = NULL; acomp->dev = NULL; + dev_priv->audio_component = NULL; + drm_modeset_unlock_all(dev_priv->dev); } static const struct component_ops i915_audio_component_bind_ops = { diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index e5d60ec..f5c9cf2 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -313,14 +313,14 @@ static void drv260x_close(struct input_dev *input) gpiod_set_value(haptics->enable_gpio, 0); } -static const struct reg_default drv260x_lra_cal_regs[] = { +static const struct reg_sequence drv260x_lra_cal_regs[] = { { DRV260X_MODE, DRV260X_AUTO_CAL }, { DRV260X_CTRL3, DRV260X_NG_THRESH_2 }, { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH }, }; -static const struct reg_default drv260x_lra_init_regs[] = { +static const struct reg_sequence drv260x_lra_init_regs[] = { { DRV260X_MODE, DRV260X_RT_PLAYBACK }, { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ }, @@ -337,7 +337,7 @@ static const struct reg_default drv260x_lra_init_regs[] = { { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, }; -static const struct reg_default drv260x_erm_cal_regs[] = { +static const struct reg_sequence drv260x_erm_cal_regs[] = { { DRV260X_MODE, DRV260X_AUTO_CAL }, { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c index 0afaa33..924456e 100644 --- a/drivers/input/misc/drv2665.c +++ b/drivers/input/misc/drv2665.c @@ -132,7 +132,7 @@ static void drv2665_close(struct input_dev *input) "Failed to enter standby mode: %d\n", error); } -static const struct reg_default drv2665_init_regs[] = { +static const struct reg_sequence drv2665_init_regs[] = { { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT }, { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN }, }; diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index fc0fddf..047136a 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -262,14 +262,14 @@ static void drv2667_close(struct input_dev *input) "Failed to enter standby mode: %d\n", error); } -static const struct reg_default drv2667_init_regs[] = { +static const struct reg_sequence drv2667_init_regs[] = { { DRV2667_CTRL_2, 0 }, { DRV2667_CTRL_1, DRV2667_25_VPP_GAIN }, { DRV2667_WV_SEQ_0, 1 }, { DRV2667_WV_SEQ_1, 0 } }; -static const struct reg_default drv2667_page1_init[] = { +static const struct reg_sequence drv2667_page1_init[] = { { DRV2667_RAM_HDR_SZ, 0x05 }, { DRV2667_RAM_START_HI, 0x80 }, { DRV2667_RAM_START_LO, 0x06 }, diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index b1ae779..1534e9b 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -732,8 +732,7 @@ static int wm97xx_remove(struct device *dev) return 0; } -#ifdef CONFIG_PM -static int wm97xx_suspend(struct device *dev, pm_message_t state) +static int __maybe_unused wm97xx_suspend(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); u16 reg; @@ -765,7 +764,7 @@ static int wm97xx_suspend(struct device *dev, pm_message_t state) return 0; } -static int wm97xx_resume(struct device *dev) +static int __maybe_unused wm97xx_resume(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); @@ -799,10 +798,7 @@ static int wm97xx_resume(struct device *dev) return 0; } -#else -#define wm97xx_suspend NULL -#define wm97xx_resume NULL -#endif +static SIMPLE_DEV_PM_OPS(wm97xx_pm_ops, wm97xx_suspend, wm97xx_resume); /* * Machine specific operations @@ -836,8 +832,7 @@ static struct device_driver wm97xx_driver = { .owner = THIS_MODULE, .probe = wm97xx_probe, .remove = wm97xx_remove, - .suspend = wm97xx_suspend, - .resume = wm97xx_resume, + .pm = &wm97xx_pm_ops, }; static int __init wm97xx_init(void) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 08b36fa..44cfdbb 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -409,7 +409,7 @@ err: * Register patch to some of the CODECs internal write sequences * to ensure a clean exit from the low power sleep state. */ -static const struct reg_default wm5110_sleep_patch[] = { +static const struct reg_sequence wm5110_sleep_patch[] = { { 0x337A, 0xC100 }, { 0x337B, 0x0041 }, { 0x3300, 0xA210 }, diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index fbc9b6e..a151ee2 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -86,7 +86,7 @@ static const struct reg_default twl6040_defaults[] = { { 0x2E, 0x00 }, /* REG_STATUS (ro) */ }; -static struct reg_default twl6040_patch[] = { +static struct reg_sequence twl6040_patch[] = { /* * Select I2C bus access to dual access registers * Interrupt register is cleared on read diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index a4997a5..0386eaf 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -21,7 +21,7 @@ #define WM5102_NUM_AOD_ISR 2 #define WM5102_NUM_ISR 5 -static const struct reg_default wm5102_reva_patch[] = { +static const struct reg_sequence wm5102_reva_patch[] = { { 0x80, 0x0003 }, { 0x221, 0x0090 }, { 0x211, 0x0014 }, @@ -57,7 +57,7 @@ static const struct reg_default wm5102_reva_patch[] = { { 0x80, 0x0000 }, }; -static const struct reg_default wm5102_revb_patch[] = { +static const struct reg_sequence wm5102_revb_patch[] = { { 0x19, 0x0001 }, { 0x80, 0x0003 }, { 0x081, 0xE022 }, @@ -80,7 +80,7 @@ static const struct reg_default wm5102_revb_patch[] = { /* We use a function so we can use ARRAY_SIZE() */ int wm5102_patch(struct arizona *arizona) { - const struct reg_default *wm5102_patch; + const struct reg_sequence *wm5102_patch; int patch_size; switch (arizona->rev) { diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 19785a6b..c4b9374 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -21,7 +21,7 @@ #define WM5110_NUM_AOD_ISR 2 #define WM5110_NUM_ISR 5 -static const struct reg_default wm5110_reva_patch[] = { +static const struct reg_sequence wm5110_reva_patch[] = { { 0x80, 0x3 }, { 0x44, 0x20 }, { 0x45, 0x40 }, @@ -134,7 +134,7 @@ static const struct reg_default wm5110_reva_patch[] = { { 0x209, 0x002A }, }; -static const struct reg_default wm5110_revb_patch[] = { +static const struct reg_sequence wm5110_revb_patch[] = { { 0x80, 0x3 }, { 0x36e, 0x0210 }, { 0x370, 0x0210 }, @@ -224,7 +224,7 @@ static const struct reg_default wm5110_revb_patch[] = { { 0x80, 0x0 }, }; -static const struct reg_default wm5110_revd_patch[] = { +static const struct reg_sequence wm5110_revd_patch[] = { { 0x80, 0x3 }, { 0x80, 0x3 }, { 0x393, 0x27 }, @@ -249,6 +249,16 @@ static const struct reg_default wm5110_revd_patch[] = { { 0x80, 0x0 }, }; +/* Add extra headphone write sequence locations */ +static const struct reg_default wm5110_reve_patch[] = { + { 0x80, 0x3 }, + { 0x80, 0x3 }, + { 0x4b, 0x138 }, + { 0x4c, 0x13d }, + { 0x80, 0x0 }, + { 0x80, 0x0 }, +}; + /* We use a function so we can use ARRAY_SIZE() */ int wm5110_patch(struct arizona *arizona) { @@ -266,7 +276,9 @@ int wm5110_patch(struct arizona *arizona) wm5110_revd_patch, ARRAY_SIZE(wm5110_revd_patch)); default: - return 0; + return regmap_register_patch(arizona->regmap, + wm5110_reve_patch, + ARRAY_SIZE(wm5110_reve_patch)); } } EXPORT_SYMBOL_GPL(wm5110_patch); @@ -676,6 +688,7 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */ { 0x00000040, 0x0000 }, /* R64 - Wake control */ { 0x00000041, 0x0000 }, /* R65 - Sequence control */ + { 0x00000042, 0x0000 }, /* R66 - Spare Triggers */ { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ @@ -1706,6 +1719,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_PWM_DRIVE_3: case ARIZONA_WAKE_CONTROL: case ARIZONA_SEQUENCE_CONTROL: + case ARIZONA_SPARE_TRIGGERS: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3: diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 213da34..7eec619 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -243,21 +243,21 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) } #endif -static const struct reg_default wm8994_revc_patch[] = { +static const struct reg_sequence wm8994_revc_patch[] = { { 0x102, 0x3 }, { 0x56, 0x3 }, { 0x817, 0x0 }, { 0x102, 0x0 }, }; -static const struct reg_default wm8958_reva_patch[] = { +static const struct reg_sequence wm8958_reva_patch[] = { { 0x102, 0x3 }, { 0xcb, 0x81 }, { 0x817, 0x0 }, { 0x102, 0x0 }, }; -static const struct reg_default wm1811_reva_patch[] = { +static const struct reg_sequence wm1811_reva_patch[] = { { 0x102, 0x3 }, { 0x56, 0xc07 }, { 0x5d, 0x7e }, @@ -326,7 +326,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) { struct wm8994_pdata *pdata; struct regmap_config *regmap_config; - const struct reg_default *regmap_patch = NULL; + const struct reg_sequence *regmap_patch = NULL; const char *devname; int ret, i, patch_regs = 0; int pulls = 0; diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index 2a91473..ca41a56 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -17,7 +17,7 @@ #include "arizona.h" -static const struct reg_default wm8997_reva_patch[] = { +static const struct reg_sequence wm8997_reva_patch[] = { { 0x80, 0x0003 }, { 0x214, 0x0008 }, { 0x458, 0x0000 }, diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h index c9a8b64..b2d56dd 100644 --- a/include/drm/i915_component.h +++ b/include/drm/i915_component.h @@ -34,6 +34,17 @@ struct i915_audio_component { void (*codec_wake_override)(struct device *, bool enable); int (*get_cdclk_freq)(struct device *); } *ops; + + const struct i915_audio_component_audio_ops { + void *audio_ptr; + /** + * Call from i915 driver, notifying the HDA driver that + * pin sense and/or ELD information has changed. + * @audio_ptr: HDA driver object + * @port: Which port has changed (PORTA / PORTB / PORTC etc) + */ + void (*pin_eld_notify)(void *audio_ptr, int port); + } *audio_ops; }; #endif /* _I915_COMPONENT_H_ */ diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 5c39dc5..fdd70b3 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -39,6 +39,7 @@ #define ARIZONA_PWM_DRIVE_3 0x32 #define ARIZONA_WAKE_CONTROL 0x40 #define ARIZONA_SEQUENCE_CONTROL 0x41 +#define ARIZONA_SPARE_TRIGGERS 0x42 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1 0x61 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63 @@ -1452,6 +1453,42 @@ #define ARIZONA_WSEQ_ENA_JD2_RISE_WIDTH 1 /* WSEQ_ENA_JD2_RISE */ /* + * R66 (0x42) - Spare Triggers + */ +#define ARIZONA_WS_TRG8 0x0080 /* WS_TRG8 */ +#define ARIZONA_WS_TRG8_MASK 0x0080 /* WS_TRG8 */ +#define ARIZONA_WS_TRG8_SHIFT 7 /* WS_TRG8 */ +#define ARIZONA_WS_TRG8_WIDTH 1 /* WS_TRG8 */ +#define ARIZONA_WS_TRG7 0x0040 /* WS_TRG7 */ +#define ARIZONA_WS_TRG7_MASK 0x0040 /* WS_TRG7 */ +#define ARIZONA_WS_TRG7_SHIFT 6 /* WS_TRG7 */ +#define ARIZONA_WS_TRG7_WIDTH 1 /* WS_TRG7 */ +#define ARIZONA_WS_TRG6 0x0020 /* WS_TRG6 */ +#define ARIZONA_WS_TRG6_MASK 0x0020 /* WS_TRG6 */ +#define ARIZONA_WS_TRG6_SHIFT 5 /* WS_TRG6 */ +#define ARIZONA_WS_TRG6_WIDTH 1 /* WS_TRG6 */ +#define ARIZONA_WS_TRG5 0x0010 /* WS_TRG5 */ +#define ARIZONA_WS_TRG5_MASK 0x0010 /* WS_TRG5 */ +#define ARIZONA_WS_TRG5_SHIFT 4 /* WS_TRG5 */ +#define ARIZONA_WS_TRG5_WIDTH 1 /* WS_TRG5 */ +#define ARIZONA_WS_TRG4 0x0008 /* WS_TRG4 */ +#define ARIZONA_WS_TRG4_MASK 0x0008 /* WS_TRG4 */ +#define ARIZONA_WS_TRG4_SHIFT 3 /* WS_TRG4 */ +#define ARIZONA_WS_TRG4_WIDTH 1 /* WS_TRG4 */ +#define ARIZONA_WS_TRG3 0x0004 /* WS_TRG3 */ +#define ARIZONA_WS_TRG3_MASK 0x0004 /* WS_TRG3 */ +#define ARIZONA_WS_TRG3_SHIFT 2 /* WS_TRG3 */ +#define ARIZONA_WS_TRG3_WIDTH 1 /* WS_TRG3 */ +#define ARIZONA_WS_TRG2 0x0002 /* WS_TRG2 */ +#define ARIZONA_WS_TRG2_MASK 0x0002 /* WS_TRG2 */ +#define ARIZONA_WS_TRG2_SHIFT 1 /* WS_TRG2 */ +#define ARIZONA_WS_TRG2_WIDTH 1 /* WS_TRG2 */ +#define ARIZONA_WS_TRG1 0x0001 /* WS_TRG1 */ +#define ARIZONA_WS_TRG1_MASK 0x0001 /* WS_TRG1 */ +#define ARIZONA_WS_TRG1_SHIFT 0 /* WS_TRG1 */ +#define ARIZONA_WS_TRG1_WIDTH 1 /* WS_TRG1 */ + +/* * R97 (0x61) - Sample Rate Sequence Select 1 */ #define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 59c55ea..4a67590 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -50,6 +50,17 @@ struct reg_default { unsigned int def; }; +/** + * Register/value pairs for sequences of writes + * + * @reg: Register address. + * @def: Register value. + */ +struct reg_sequence { + unsigned int reg; + unsigned int def; +}; + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -410,10 +421,10 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); -int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, +int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs); int regmap_multi_reg_write_bypassed(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); @@ -424,6 +435,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count); int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); +int regmap_write_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val); int regmap_update_bits_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); int regmap_update_bits_check(struct regmap *map, unsigned int reg, @@ -450,7 +463,7 @@ void regcache_mark_dirty(struct regmap *map); bool regmap_check_range_table(struct regmap *map, unsigned int reg, const struct regmap_access_table *table); -int regmap_register_patch(struct regmap *map, const struct reg_default *regs, +int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs, int num_regs); int regmap_parse_val(struct regmap *map, const void *buf, unsigned int *val); @@ -503,6 +516,8 @@ int regmap_field_update_bits(struct regmap_field *field, int regmap_fields_write(struct regmap_field *field, unsigned int id, unsigned int val); +int regmap_fields_force_write(struct regmap_field *field, unsigned int id, + unsigned int val); int regmap_fields_read(struct regmap_field *field, unsigned int id, unsigned int *val); int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, @@ -645,6 +660,13 @@ static inline int regmap_update_bits(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_write_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_update_bits_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 0e9d75b..74bc8547 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -584,6 +584,8 @@ static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, void snd_ac97_suspend(struct snd_ac97 *ac97); void snd_ac97_resume(struct snd_ac97 *ac97); #endif +int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, + unsigned int id_mask); /* quirk types */ enum { diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h index adb5ba5..930b41e 100644 --- a/include/sound/hda_i915.h +++ b/include/sound/hda_i915.h @@ -4,14 +4,17 @@ #ifndef __SOUND_HDA_I915_H #define __SOUND_HDA_I915_H +#include <drm/i915_component.h> + #ifdef CONFIG_SND_HDA_I915 int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable); int snd_hdac_display_power(struct hdac_bus *bus, bool enable); int snd_hdac_get_display_clk(struct hdac_bus *bus); int snd_hdac_i915_init(struct hdac_bus *bus); int snd_hdac_i915_exit(struct hdac_bus *bus); +int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *); #else -static int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) +static inline int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) { return 0; } @@ -31,6 +34,10 @@ static inline int snd_hdac_i915_exit(struct hdac_bus *bus) { return 0; } +static inline int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *ops) +{ + return -ENODEV; +} #endif #endif /* __SOUND_HDA_I915_H */ diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index ae995e5..2ae8812 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -160,6 +160,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_SPB_BASE 0x08 /* Interval used to calculate the iterating register offset */ #define AZX_SPB_INTERVAL 0x08 +/* SPIB base */ +#define AZX_SPB_SPIB 0x00 +/* SPIB MAXFIFO base*/ +#define AZX_SPB_MAXFIFO 0x04 /* registers of Global Time Synchronization Capability Structure */ #define AZX_GTS_CAP_ID 0x1 diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 4caf1fd..49bc836 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -119,6 +119,7 @@ int snd_hdac_device_register(struct hdac_device *codec); void snd_hdac_device_unregister(struct hdac_device *codec); int snd_hdac_refresh_widgets(struct hdac_device *codec); +int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec); unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm); @@ -164,15 +165,15 @@ static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, } #ifdef CONFIG_PM -void snd_hdac_power_up(struct hdac_device *codec); -void snd_hdac_power_down(struct hdac_device *codec); -void snd_hdac_power_up_pm(struct hdac_device *codec); -void snd_hdac_power_down_pm(struct hdac_device *codec); +int snd_hdac_power_up(struct hdac_device *codec); +int snd_hdac_power_down(struct hdac_device *codec); +int snd_hdac_power_up_pm(struct hdac_device *codec); +int snd_hdac_power_down_pm(struct hdac_device *codec); #else -static inline void snd_hdac_power_up(struct hdac_device *codec) {} -static inline void snd_hdac_power_down(struct hdac_device *codec) {} -static inline void snd_hdac_power_up_pm(struct hdac_device *codec) {} -static inline void snd_hdac_power_down_pm(struct hdac_device *codec) {} +static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; } +static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; } +static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; } +static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; } #endif /* @@ -437,6 +438,8 @@ void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream); void snd_hdac_stream_release(struct hdac_stream *azx_dev); +struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus, + int dir, int stream_tag); int snd_hdac_stream_setup(struct hdac_stream *azx_dev); void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev); diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 0f89df1..94210dc 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -34,6 +34,7 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev, void snd_hdac_ext_bus_exit(struct hdac_ext_bus *sbus); int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *sbus, int addr); void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev); +void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); #define ebus_to_hbus(ebus) (&(ebus)->bus) #define hbus_to_ebus(_bus) \ @@ -62,6 +63,8 @@ enum hdac_ext_stream_type { * @hstream: hdac_stream * @pphc_addr: processing pipe host stream pointer * @pplc_addr: processing pipe link stream pointer + * @spib_addr: software position in buffers stream pointer + * @fifo_addr: software position Max fifos stream pointer * @decoupled: stream host and link is decoupled * @link_locked: link is locked * @link_prepared: link is prepared @@ -73,6 +76,9 @@ struct hdac_ext_stream { void __iomem *pphc_addr; void __iomem *pplc_addr; + void __iomem *spib_addr; + void __iomem *fifo_addr; + bool decoupled:1; bool link_locked:1; bool link_prepared; @@ -99,6 +105,11 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); void snd_hdac_ext_stop_streams(struct hdac_ext_bus *sbus); +int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *stream, u32 value); +int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *stream); + void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hstream); void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hstream); void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hstream); @@ -115,6 +126,7 @@ struct hdac_ext_link { int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link); int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link); +int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus); void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, int stream); void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, @@ -129,4 +141,63 @@ void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, writew(((readw(addr + reg) & ~(mask)) | (val)), \ addr + reg) + +struct hdac_ext_device; + +/* ops common to all codec drivers */ +struct hdac_ext_codec_ops { + int (*build_controls)(struct hdac_ext_device *dev); + int (*init)(struct hdac_ext_device *dev); + void (*free)(struct hdac_ext_device *dev); +}; + +struct hda_dai_map { + char *dai_name; + hda_nid_t nid; + u32 maxbps; +}; + +#define HDA_MAX_NIDS 16 + +/** + * struct hdac_ext_device - HDAC Ext device + * + * @hdac: hdac core device + * @nid_list - the dai map which matches the dai-name with the nid + * @map_cur_idx - the idx in use in dai_map + * @ops - the hda codec ops common to all codec drivers + * @pvt_data - private data, for asoc contains asoc codec object + */ +struct hdac_ext_device { + struct hdac_device hdac; + struct hdac_ext_bus *ebus; + + /* soc-dai to nid map */ + struct hda_dai_map nid_list[HDA_MAX_NIDS]; + unsigned int map_cur_idx; + + /* codec ops */ + struct hdac_ext_codec_ops ops; + + void *private_data; +}; + +#define to_ehdac_device(dev) (container_of((dev), \ + struct hdac_ext_device, hdac)) +/* + * HD-audio codec base driver + */ +struct hdac_ext_driver { + struct hdac_driver hdac; + + int (*probe)(struct hdac_ext_device *dev); + int (*remove)(struct hdac_ext_device *dev); + void (*shutdown)(struct hdac_ext_device *dev); +}; + +int snd_hda_ext_driver_register(struct hdac_ext_driver *drv); +void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv); + +#define to_ehdac_driver(_drv) container_of(_drv, struct hdac_ext_driver, hdac) + #endif /* __SOUND_HDAUDIO_EXT_H */ diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 4cecd0c..bb7b2eb 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -61,6 +61,14 @@ struct rsnd_src_platform_info { /* * flags */ +struct rsnd_ctu_platform_info { + u32 flags; +}; + +struct rsnd_mix_platform_info { + u32 flags; +}; + struct rsnd_dvc_platform_info { u32 flags; }; @@ -68,6 +76,8 @@ struct rsnd_dvc_platform_info { struct rsnd_dai_path_info { struct rsnd_ssi_platform_info *ssi; struct rsnd_src_platform_info *src; + struct rsnd_ctu_platform_info *ctu; + struct rsnd_mix_platform_info *mix; struct rsnd_dvc_platform_info *dvc; }; @@ -93,6 +103,10 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_src_platform_info *src_info; int src_info_nr; + struct rsnd_ctu_platform_info *ctu_info; + int ctu_info_nr; + struct rsnd_mix_platform_info *mix_info; + int mix_info_nr; struct rsnd_dvc_platform_info *dvc_info; int dvc_info_nr; struct rsnd_dai_platform_info *dai_info; diff --git a/include/sound/rt298.h b/include/sound/rt298.h new file mode 100644 index 0000000..7fffeaa --- /dev/null +++ b/include/sound/rt298.h @@ -0,0 +1,20 @@ +/* + * linux/sound/rt286.h -- Platform data for RT286 + * + * Copyright 2013 Realtek Microelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_RT298_H +#define __LINUX_SND_RT298_H + +struct rt298_platform_data { + bool cbj_en; /*combo jack enable*/ + bool gpio2_en; /*GPIO2 enable*/ + bool suspend_power_off; /* power is off during suspend */ +}; + +#endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 37d95a8..5abba03 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -397,6 +397,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); /* dapm events */ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, @@ -511,9 +512,18 @@ struct snd_soc_dapm_route { struct snd_soc_dapm_path { const char *name; - /* source (input) and sink (output) widgets */ - struct snd_soc_dapm_widget *source; - struct snd_soc_dapm_widget *sink; + /* + * source (input) and sink (output) widgets + * The union is for convience, since it is a lot nicer to type + * p->source, rather than p->node[SND_SOC_DAPM_DIR_IN] + */ + union { + struct { + struct snd_soc_dapm_widget *source; + struct snd_soc_dapm_widget *sink; + }; + struct snd_soc_dapm_widget *node[2]; + }; /* status */ u32 connect:1; /* source and sink widgets are connected */ @@ -524,8 +534,7 @@ struct snd_soc_dapm_path { int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); - struct list_head list_source; - struct list_head list_sink; + struct list_head list_node[2]; struct list_head list_kcontrol; struct list_head list; }; @@ -559,8 +568,7 @@ struct snd_soc_dapm_widget { unsigned char new_power:1; /* power from this run */ unsigned char power_checked:1; /* power checked this run */ unsigned char is_supply:1; /* Widget is a supply type widget */ - unsigned char is_sink:1; /* Widget is a sink type widget */ - unsigned char is_source:1; /* Widget is a source type widget */ + unsigned char is_ep:2; /* Widget is a endpoint type widget */ int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); @@ -575,16 +583,14 @@ struct snd_soc_dapm_widget { struct snd_kcontrol **kcontrols; struct snd_soc_dobj dobj; - /* widget input and outputs */ - struct list_head sources; - struct list_head sinks; + /* widget input and output edges */ + struct list_head edges[2]; /* used during DAPM updates */ struct list_head work_list; struct list_head power_list; struct list_head dirty; - int inputs; - int outputs; + int endpoints[2]; struct clk *clk; }; @@ -672,4 +678,58 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( return dapm->bias_level; } +enum snd_soc_dapm_direction { + SND_SOC_DAPM_DIR_IN, + SND_SOC_DAPM_DIR_OUT +}; + +#define SND_SOC_DAPM_DIR_TO_EP(x) BIT(x) + +#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) +#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) + +/** + * snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths in the + * specified direction of a widget + * @w: The widget + * @dir: Whether to iterate over the paths where the specified widget is the + * incoming or outgoing widgets + * @p: The path iterator variable + */ +#define snd_soc_dapm_widget_for_each_path(w, dir, p) \ + list_for_each_entry(p, &w->edges[dir], list_node[dir]) + +/** + * snd_soc_dapm_widget_for_each_sink_path_safe - Iterates over all paths in the + * specified direction of a widget + * @w: The widget + * @dir: Whether to iterate over the paths where the specified widget is the + * incoming or outgoing widgets + * @p: The path iterator variable + * @next_p: Temporary storage for the next path + * + * This function works like snd_soc_dapm_widget_for_each_sink_path, expect that + * it is safe to remove the current path from the list while iterating + */ +#define snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) \ + list_for_each_entry_safe(p, next_p, &w->edges[dir], list_node[dir]) + +/** + * snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths leaving a + * widget + * @w: The widget + * @p: The path iterator variable + */ +#define snd_soc_dapm_widget_for_each_sink_path(w, p) \ + snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_IN, p) + +/** + * snd_soc_dapm_widget_for_each_source_path - Iterates over all paths leading to + * a widget + * @w: The widget + * @p: The path iterator variable + */ +#define snd_soc_dapm_widget_for_each_source_path(w, p) \ + snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_OUT, p) + #endif diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 427bc41..086cd7f 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -89,6 +89,13 @@ struct snd_soc_tplg_kcontrol_ops { struct snd_ctl_elem_info *uinfo); }; +/* Bytes ext operations, for TLV byte controls */ +struct snd_soc_tplg_bytes_ext_ops { + u32 id; + int (*get)(unsigned int __user *bytes, unsigned int size); + int (*put)(const unsigned int __user *bytes, unsigned int size); +}; + /* * DAPM widget event handlers - used to map handlers onto widgets. */ @@ -136,9 +143,13 @@ struct snd_soc_tplg_ops { int (*manifest)(struct snd_soc_component *, struct snd_soc_tplg_manifest *); - /* bespoke kcontrol handlers available for binding */ + /* vendor specific kcontrol handlers available for binding */ const struct snd_soc_tplg_kcontrol_ops *io_ops; int io_ops_count; + + /* vendor specific bytes ext handlers available for binding */ + const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops; + int bytes_ext_ops_count; }; #ifdef CONFIG_SND_SOC_TOPOLOGY diff --git a/include/sound/soc.h b/include/sound/soc.h index 93df8bf..884e728 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -526,7 +526,8 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, #ifdef CONFIG_SND_SOC_AC97_BUS struct snd_ac97 *snd_soc_alloc_ac97_codec(struct snd_soc_codec *codec); -struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec); +struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec, + unsigned int id, unsigned int id_mask); void snd_soc_free_ac97_codec(struct snd_ac97 *ac97); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); @@ -619,6 +620,7 @@ int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, * @pin: name of the pin to update * @mask: bits to check for in reported jack status * @invert: if non-zero then pin is enabled when status is not reported + * @list: internal list entry */ struct snd_soc_jack_pin { struct list_head list; @@ -635,7 +637,7 @@ struct snd_soc_jack_pin { * @jack_type: type of jack that is expected for this voltage * @debounce_time: debounce_time for jack, codec driver should wait for this * duration before reading the adc for voltages - * @:list: list container + * @list: internal list entry */ struct snd_soc_jack_zone { unsigned int min_mv; @@ -651,12 +653,12 @@ struct snd_soc_jack_zone { * @gpio: legacy gpio number * @idx: gpio descriptor index within the function of the GPIO * consumer device - * @gpiod_dev GPIO consumer device + * @gpiod_dev: GPIO consumer device * @name: gpio name. Also as connection ID for the GPIO consumer * device function name lookup * @report: value to report when jack detected * @invert: report presence in low state - * @debouce_time: debouce time in ms + * @debounce_time: debounce time in ms * @wake: enable as wake source * @jack_status_check: callback function which overrides the detection * to provide more complex checks (eg, reading an @@ -672,11 +674,13 @@ struct snd_soc_jack_gpio { int debounce_time; bool wake; + /* private: */ struct snd_soc_jack *jack; struct delayed_work work; struct gpio_desc *desc; void *data; + /* public: */ int (*jack_status_check)(void *data); }; @@ -758,7 +762,6 @@ struct snd_soc_component { unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ unsigned int registered_as_component:1; - unsigned int probed:1; struct list_head list; @@ -792,7 +795,6 @@ struct snd_soc_component { /* Don't use these, use snd_soc_component_get_dapm() */ struct snd_soc_dapm_context dapm; - struct snd_soc_dapm_context *dapm_ptr; const struct snd_kcontrol_new *controls; unsigned int num_controls; @@ -832,9 +834,6 @@ struct snd_soc_codec { /* component */ struct snd_soc_component component; - /* Don't access this directly, use snd_soc_codec_get_dapm() */ - struct snd_soc_dapm_context dapm; - #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_reg; #endif @@ -1277,7 +1276,7 @@ static inline struct snd_soc_component *snd_soc_dapm_to_component( static inline struct snd_soc_codec *snd_soc_dapm_to_codec( struct snd_soc_dapm_context *dapm) { - return container_of(dapm, struct snd_soc_codec, dapm); + return snd_soc_component_to_codec(snd_soc_dapm_to_component(dapm)); } /** @@ -1302,7 +1301,7 @@ static inline struct snd_soc_platform *snd_soc_dapm_to_platform( static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm( struct snd_soc_component *component) { - return component->dapm_ptr; + return &component->dapm; } /** @@ -1314,12 +1313,12 @@ static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm( static inline struct snd_soc_dapm_context *snd_soc_codec_get_dapm( struct snd_soc_codec *codec) { - return &codec->dapm; + return snd_soc_component_get_dapm(&codec->component); } /** * snd_soc_dapm_init_bias_level() - Initialize CODEC DAPM bias level - * @dapm: The CODEC for which to initialize the DAPM bias level + * @codec: The CODEC for which to initialize the DAPM bias level * @level: The DAPM level to initialize to * * Initializes the CODEC DAPM bias level. See snd_soc_dapm_init_bias_level(). @@ -1604,6 +1603,10 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, int snd_soc_of_parse_tdm_slot(struct device_node *np, unsigned int *slots, unsigned int *slot_width); +void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, + struct snd_soc_codec_conf *codec_conf, + struct device_node *of_node, + const char *propname); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 88cf39d..317a1ed 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -8,6 +8,7 @@ #include <linux/tracepoint.h> #define DAPM_DIRECT "(direct)" +#define DAPM_ARROW(dir) (((dir) == SND_SOC_DAPM_DIR_OUT) ? "->" : "<-") struct snd_soc_jack; struct snd_soc_codec; @@ -152,62 +153,38 @@ TRACE_EVENT(snd_soc_dapm_walk_done, (int)__entry->path_checks, (int)__entry->neighbour_checks) ); -TRACE_EVENT(snd_soc_dapm_output_path, +TRACE_EVENT(snd_soc_dapm_path, TP_PROTO(struct snd_soc_dapm_widget *widget, + enum snd_soc_dapm_direction dir, struct snd_soc_dapm_path *path), - TP_ARGS(widget, path), + TP_ARGS(widget, dir, path), TP_STRUCT__entry( __string( wname, widget->name ) __string( pname, path->name ? path->name : DAPM_DIRECT) - __string( psname, path->sink->name ) - __field( int, path_sink ) + __string( pnname, path->node[dir]->name ) + __field( int, path_node ) __field( int, path_connect ) + __field( int, path_dir ) ), TP_fast_assign( __assign_str(wname, widget->name); __assign_str(pname, path->name ? path->name : DAPM_DIRECT); - __assign_str(psname, path->sink->name); + __assign_str(pnname, path->node[dir]->name); __entry->path_connect = path->connect; - __entry->path_sink = (long)path->sink; + __entry->path_node = (long)path->node[dir]; + __entry->path_dir = dir; ), - TP_printk("%c%s -> %s -> %s", - (int) __entry->path_sink && + TP_printk("%c%s %s %s %s %s", + (int) __entry->path_node && (int) __entry->path_connect ? '*' : ' ', - __get_str(wname), __get_str(pname), __get_str(psname)) -); - -TRACE_EVENT(snd_soc_dapm_input_path, - - TP_PROTO(struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_path *path), - - TP_ARGS(widget, path), - - TP_STRUCT__entry( - __string( wname, widget->name ) - __string( pname, path->name ? path->name : DAPM_DIRECT) - __string( psname, path->source->name ) - __field( int, path_source ) - __field( int, path_connect ) - ), - - TP_fast_assign( - __assign_str(wname, widget->name); - __assign_str(pname, path->name ? path->name : DAPM_DIRECT); - __assign_str(psname, path->source->name); - __entry->path_connect = path->connect; - __entry->path_source = (long)path->source; - ), - - TP_printk("%c%s <- %s <- %s", - (int) __entry->path_source && - (int) __entry->path_connect ? '*' : ' ', - __get_str(wname), __get_str(pname), __get_str(psname)) + __get_str(wname), DAPM_ARROW(__entry->path_dir), + __get_str(pname), DAPM_ARROW(__entry->path_dir), + __get_str(pnname)) ); TRACE_EVENT(snd_soc_dapm_connected, diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c index 2b50cbe..52e4bc5 100644 --- a/sound/ac97_bus.c +++ b/sound/ac97_bus.c @@ -18,44 +18,80 @@ #include <sound/ac97_codec.h> /* - * Let drivers decide whether they want to support given codec from their - * probe method. Drivers have direct access to the struct snd_ac97 - * structure and may decide based on the id field amongst other things. + * snd_ac97_check_id() - Reads and checks the vendor ID of the device + * @ac97: The AC97 device to check + * @id: The ID to compare to + * @id_mask: Mask that is applied to the device ID before comparing to @id + * + * If @id is 0 this function returns true if the read device vendor ID is + * a valid ID. If @id is non 0 this functions returns true if @id + * matches the read vendor ID. Otherwise the function returns false. */ -static int ac97_bus_match(struct device *dev, struct device_driver *drv) +static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id, + unsigned int id_mask) { - return 1; -} + ac97->id = ac97->bus->ops->read(ac97, AC97_VENDOR_ID1) << 16; + ac97->id |= ac97->bus->ops->read(ac97, AC97_VENDOR_ID2); -#ifdef CONFIG_PM -static int ac97_bus_suspend(struct device *dev, pm_message_t state) -{ - int ret = 0; + if (ac97->id == 0x0 || ac97->id == 0xffffffff) + return false; - if (dev->driver && dev->driver->suspend) - ret = dev->driver->suspend(dev, state); + if (id != 0 && id != (ac97->id & id_mask)) + return false; - return ret; + return true; } -static int ac97_bus_resume(struct device *dev) +/** + * snd_ac97_reset() - Reset AC'97 device + * @ac97: The AC'97 device to reset + * @try_warm: Try a warm reset first + * @id: Expected device vendor ID + * @id_mask: Mask that is applied to the device ID before comparing to @id + * + * This function resets the AC'97 device. If @try_warm is true the function + * first performs a warm reset. If the warm reset is successful the function + * returns 1. Otherwise or if @try_warm is false the function issues cold reset + * followed by a warm reset. If this is successful the function returns 0, + * otherwise a negative error code. If @id is 0 any valid device ID will be + * accepted, otherwise only the ID that matches @id and @id_mask is accepted. + */ +int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, + unsigned int id_mask) { - int ret = 0; + struct snd_ac97_bus_ops *ops = ac97->bus->ops; + + if (try_warm && ops->warm_reset) { + ops->warm_reset(ac97); + if (snd_ac97_check_id(ac97, id, id_mask)) + return 1; + } - if (dev->driver && dev->driver->resume) - ret = dev->driver->resume(dev); + if (ops->reset) + ops->reset(ac97); + if (ops->warm_reset) + ops->warm_reset(ac97); - return ret; + if (snd_ac97_check_id(ac97, id, id_mask)) + return 0; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(snd_ac97_reset); + +/* + * Let drivers decide whether they want to support given codec from their + * probe method. Drivers have direct access to the struct snd_ac97 + * structure and may decide based on the id field amongst other things. + */ +static int ac97_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; } -#endif /* CONFIG_PM */ struct bus_type ac97_bus_type = { .name = "ac97", .match = ac97_bus_match, -#ifdef CONFIG_PM - .suspend = ac97_bus_suspend, - .resume = ac97_bus_resume, -#endif /* CONFIG_PM */ }; static int __init ac97_bus_init(void) diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 23c371ec..a04edff 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -1050,7 +1050,6 @@ MODULE_DEVICE_TABLE(i2c,onyx_i2c_id); static struct i2c_driver onyx_driver = { .driver = { .name = "aoa_codec_onyx", - .owner = THIS_MODULE, }, .probe = onyx_i2c_probe, .remove = onyx_i2c_remove, diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index 364c7c4..78ed1ff 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -939,7 +939,6 @@ MODULE_DEVICE_TABLE(i2c,tas_i2c_id); static struct i2c_driver tas_driver = { .driver = { .name = "aoa_codec_tas", - .owner = THIS_MODULE, }, .probe = tas_i2c_probe, .remove = tas_i2c_remove, diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 9dc5806..8f71f7e 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -1120,10 +1120,10 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) return 0; } -#ifdef CONFIG_PM -static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int aoa_fabric_layout_suspend(struct device *dev) { - struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); + struct layout_dev *ldev = dev_get_drvdata(dev); if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) ldev->gpio.methods->all_amps_off(&ldev->gpio); @@ -1131,15 +1131,19 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta return 0; } -static int aoa_fabric_layout_resume(struct soundbus_dev *sdev) +static int aoa_fabric_layout_resume(struct device *dev) { - struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); + struct layout_dev *ldev = dev_get_drvdata(dev); if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore) ldev->gpio.methods->all_amps_restore(&ldev->gpio); return 0; } + +static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, + aoa_fabric_layout_suspend, aoa_fabric_layout_resume); + #endif static struct soundbus_driver aoa_soundbus_driver = { @@ -1147,12 +1151,11 @@ static struct soundbus_driver aoa_soundbus_driver = { .owner = THIS_MODULE, .probe = aoa_fabric_layout_probe, .remove = aoa_fabric_layout_remove, -#ifdef CONFIG_PM - .suspend = aoa_fabric_layout_suspend, - .resume = aoa_fabric_layout_resume, -#endif .driver = { .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &aoa_fabric_layout_pm_ops, +#endif } }; diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 3edf736..70bcaa7 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -126,30 +126,6 @@ static void soundbus_device_shutdown(struct device *dev) drv->shutdown(soundbus_dev); } -#ifdef CONFIG_PM - -static int soundbus_device_suspend(struct device *dev, pm_message_t state) -{ - struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); - struct soundbus_driver * drv = to_soundbus_driver(dev->driver); - - if (dev->driver && drv->suspend) - return drv->suspend(soundbus_dev, state); - return 0; -} - -static int soundbus_device_resume(struct device * dev) -{ - struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); - struct soundbus_driver * drv = to_soundbus_driver(dev->driver); - - if (dev->driver && drv->resume) - return drv->resume(soundbus_dev); - return 0; -} - -#endif /* CONFIG_PM */ - /* soundbus_dev_attrs is declared in sysfs.c */ ATTRIBUTE_GROUPS(soundbus_dev); static struct bus_type soundbus_bus_type = { @@ -158,10 +134,6 @@ static struct bus_type soundbus_bus_type = { .uevent = soundbus_uevent, .remove = soundbus_device_remove, .shutdown = soundbus_device_shutdown, -#ifdef CONFIG_PM - .suspend = soundbus_device_suspend, - .resume = soundbus_device_resume, -#endif .dev_groups = soundbus_dev_groups, }; diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h index 21e756c..ae40224 100644 --- a/sound/aoa/soundbus/soundbus.h +++ b/sound/aoa/soundbus/soundbus.h @@ -188,8 +188,6 @@ struct soundbus_driver { int (*probe)(struct soundbus_dev* dev); int (*remove)(struct soundbus_dev* dev); - int (*suspend)(struct soundbus_dev* dev, pm_message_t state); - int (*resume)(struct soundbus_dev* dev); int (*shutdown)(struct soundbus_dev* dev); struct device_driver driver; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 7a2c1f5..c0f018a 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -211,26 +211,38 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&bebob->substreams_counter); amdtp_stream_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + + return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&bebob->substreams_counter); amdtp_stream_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + + return 0; } static int diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index f7771451..4e67b1d 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -230,6 +230,12 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); @@ -240,13 +246,18 @@ static int capture_hw_params(struct snd_pcm_substream *substream, amdtp_stream_set_pcm_format(&dice->tx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return 0; } static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); @@ -257,8 +268,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return 0; } static int capture_hw_free(struct snd_pcm_substream *substream) diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 8a34753..c30b2ff 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -244,25 +244,35 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_efw *efw = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&efw->capture_substreams); amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_efw *efw = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&efw->playback_substreams); amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 67ade07..9c73930 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -231,7 +231,12 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_oxfw *oxfw = substream->private_data; + int err; + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&oxfw->mutex); @@ -241,13 +246,18 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_oxfw *oxfw = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&oxfw->mutex); @@ -257,8 +267,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 873d40f..77ad5b9 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -512,12 +512,11 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, if (err < 0) goto end; - formats[eid] = kmalloc(*len, GFP_KERNEL); + formats[eid] = kmemdup(buf, *len, GFP_KERNEL); if (formats[eid] == NULL) { err = -ENOMEM; goto end; } - memcpy(formats[eid], buf, *len); /* apply the format for each available sampling rate */ for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) { @@ -531,12 +530,11 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, continue; eid++; - formats[eid] = kmalloc(*len, GFP_KERNEL); + formats[eid] = kmemdup(buf, *len, GFP_KERNEL); if (formats[eid] == NULL) { err = -ENOMEM; goto end; } - memcpy(formats[eid], buf, *len); formats[eid][2] = avc_stream_rate_table[i]; } @@ -594,12 +592,11 @@ static int fill_stream_formats(struct snd_oxfw *oxfw, if (err < 0) break; - formats[eid] = kmalloc(len, GFP_KERNEL); + formats[eid] = kmemdup(buf, len, GFP_KERNEL); if (formats[eid] == NULL) { err = -ENOMEM; break; } - memcpy(formats[eid], buf, len); /* get next entry */ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 0aa5d9e..4449d1a9 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -125,7 +125,7 @@ static void default_release(struct device *dev) } /** - * snd_hdac_ext_device_init - initialize the HDA extended codec base device + * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device * @ebus: hdac extended bus to attach to * @addr: codec address * @@ -133,14 +133,16 @@ static void default_release(struct device *dev) */ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) { + struct hdac_ext_device *edev; struct hdac_device *hdev = NULL; struct hdac_bus *bus = ebus_to_hbus(ebus); char name[15]; int ret; - hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); - if (!hdev) + edev = kzalloc(sizeof(*edev), GFP_KERNEL); + if (!edev) return -ENOMEM; + hdev = &edev->hdac; snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); @@ -158,6 +160,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) snd_hdac_ext_bus_device_exit(hdev); return ret; } + return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); @@ -168,7 +171,94 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); */ void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) { + struct hdac_ext_device *edev = to_ehdac_device(hdev); + snd_hdac_device_exit(hdev); - kfree(hdev); + kfree(edev); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); + +/** + * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices + * + * @ebus: HD-audio extended bus + */ +void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) +{ + struct hdac_device *codec, *__codec; + /* + * we need to remove all the codec devices objects created in the + * snd_hdac_ext_bus_device_init + */ + list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { + snd_hdac_device_unregister(codec); + put_device(&codec->dev); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); +#define dev_to_hdac(dev) (container_of((dev), \ + struct hdac_device, dev)) + +static inline struct hdac_ext_driver *get_edrv(struct device *dev) +{ + struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); + struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); + + return edrv; +} + +static inline struct hdac_ext_device *get_edev(struct device *dev) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_ext_device *edev = to_ehdac_device(hdev); + + return edev; +} + +static int hda_ext_drv_probe(struct device *dev) +{ + return (get_edrv(dev))->probe(get_edev(dev)); +} + +static int hdac_ext_drv_remove(struct device *dev) +{ + return (get_edrv(dev))->remove(get_edev(dev)); +} + +static void hdac_ext_drv_shutdown(struct device *dev) +{ + return (get_edrv(dev))->shutdown(get_edev(dev)); +} + +/** + * snd_hda_ext_driver_register - register a driver for ext hda devices + * + * @drv: ext hda driver structure + */ +int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) +{ + drv->hdac.type = HDA_DEV_ASOC; + drv->hdac.driver.bus = &snd_hda_bus_type; + /* we use default match */ + + if (drv->probe) + drv->hdac.driver.probe = hda_ext_drv_probe; + if (drv->remove) + drv->hdac.driver.remove = hdac_ext_drv_remove; + if (drv->shutdown) + drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; + + return driver_register(&drv->hdac.driver); +} +EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); + +/** + * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices + * + * @drv: ext hda driver structure + */ +void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) +{ + driver_unregister(&drv->hdac.driver); +} +EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 358f161..63215b1 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -177,8 +177,8 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) hlink->bus = bus; hlink->ml_addr = ebus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * idx); - hlink->lcaps = snd_hdac_chip_readl(bus, ML_LCAP); - hlink->lsdiid = snd_hdac_chip_readw(bus, ML_LSDIID); + hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); + hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); list_add_tail(&hlink->list, &ebus->hlink_list); } @@ -243,7 +243,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable) timeout = 50; do { - val = snd_hdac_chip_readl(link->bus, ML_LCTL); + val = readl(link->ml_addr + AZX_REG_ML_LCTL); if (enable) { if (((val & mask) >> AZX_MLCTL_CPA)) return 0; @@ -263,7 +263,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable) */ int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link) { - snd_hdac_chip_updatel(link->bus, ML_LCTL, 0, AZX_MLCTL_SPA); + snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA); return check_hdac_link_power_active(link, true); } @@ -275,8 +275,28 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up); */ int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link) { - snd_hdac_chip_updatel(link->bus, ML_LCTL, AZX_MLCTL_SPA, 0); + snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0); return check_hdac_link_power_active(link, false); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down); + +/** + * snd_hdac_ext_bus_link_power_down_all -power down all hda link + * @ebus: HD-audio extended bus + */ +int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) +{ + struct hdac_ext_link *hlink = NULL; + int ret; + + list_for_each_entry(hlink, &ebus->hlink_list, list) { + snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0); + ret = check_hdac_link_power_active(hlink, false); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 3de47dd..33ba77d 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -49,6 +49,16 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, AZX_PPLC_INTERVAL * idx; } + if (ebus->spbcap) { + stream->spib_addr = ebus->spbcap + AZX_SPB_BASE + + AZX_SPB_INTERVAL * idx + + AZX_SPB_SPIB; + + stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE + + AZX_SPB_INTERVAL * idx + + AZX_SPB_MAXFIFO; + } + stream->decoupled = false; snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); } @@ -281,17 +291,12 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; struct hdac_bus *hbus = &ebus->bus; - int key; if (!ebus->ppcap) { dev_err(hbus->dev, "stream type not supported\n"); return NULL; } - /* make a non-zero unique key for the substream */ - key = (substream->pcm->device << 16) | (substream->number << 2) | - (substream->stream + 1); - list_for_each_entry(stream, &hbus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, @@ -310,7 +315,6 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, spin_lock_irq(&hbus->reg_lock); res->hstream.opened = 1; res->hstream.running = 0; - res->hstream.assigned_key = key; res->hstream.substream = substream; spin_unlock_irq(&hbus->reg_lock); } @@ -423,7 +427,7 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, mask |= (1 << index); - register_mask = snd_hdac_chip_readl(bus, SPB_SPBFCCTL); + register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL); mask |= register_mask; @@ -435,6 +439,50 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); /** + * snd_hdac_ext_stream_set_spib - sets the spib value of a stream + * @ebus: HD-audio ext core bus + * @stream: hdac_ext_stream + * @value: spib value to set + */ +int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *stream, u32 value) +{ + struct hdac_bus *bus = &ebus->bus; + + if (!ebus->spbcap) { + dev_err(bus->dev, "Address of SPB capability is NULL"); + return -EINVAL; + } + + writel(value, stream->spib_addr); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); + +/** + * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream + * @ebus: HD-audio ext core bus + * @stream: hdac_ext_stream + * + * Return maxfifo for the stream + */ +int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *stream) +{ + struct hdac_bus *bus = &ebus->bus; + + if (!ebus->spbcap) { + dev_err(bus->dev, "Address of SPB capability is NULL"); + return -EINVAL; + } + + return readl(stream->fifo_addr); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); + + +/** * snd_hdac_ext_stop_streams - stop all stream if running * @ebus: HD-audio ext core bus */ diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index cdee710..db96042 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -372,6 +372,36 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); +/** + * snd_hdac_refresh_widget_sysfs - Reset the codec widgets and reinit the + * codec sysfs + * @codec: the codec object + * + * first we need to remove sysfs, then refresh widgets and lastly + * recreate it + */ +int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec) +{ + int ret; + + if (device_is_registered(&codec->dev)) + hda_widget_sysfs_exit(codec); + ret = snd_hdac_refresh_widgets(codec); + if (ret) { + dev_err(&codec->dev, "failed to refresh widget: %d\n", ret); + return ret; + } + if (device_is_registered(&codec->dev)) { + ret = hda_widget_sysfs_init(codec); + if (ret) { + dev_err(&codec->dev, "failed to init sysfs: %d\n", ret); + return ret; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(snd_hdac_refresh_widget_sysfs); + /* return CONNLIST_LEN parameter of the given widget */ static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) { @@ -501,23 +531,27 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_connections); * This function calls the runtime PM helper to power up the given codec. * Unlike snd_hdac_power_up_pm(), you should call this only for the code * path that isn't included in PM path. Otherwise it gets stuck. + * + * Returns zero if successful, or a negative error code. */ -void snd_hdac_power_up(struct hdac_device *codec) +int snd_hdac_power_up(struct hdac_device *codec) { - pm_runtime_get_sync(&codec->dev); + return pm_runtime_get_sync(&codec->dev); } EXPORT_SYMBOL_GPL(snd_hdac_power_up); /** * snd_hdac_power_down - power down the codec * @codec: the codec object + * + * Returns zero if successful, or a negative error code. */ -void snd_hdac_power_down(struct hdac_device *codec) +int snd_hdac_power_down(struct hdac_device *codec) { struct device *dev = &codec->dev; pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); + return pm_runtime_put_autosuspend(dev); } EXPORT_SYMBOL_GPL(snd_hdac_power_down); @@ -529,11 +563,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_power_down); * which may be called by PM suspend/resume again. OTOH, if a power-up * call must wake up the sleeper (e.g. in a kctl callback), use * snd_hdac_power_up() instead. + * + * Returns zero if successful, or a negative error code. */ -void snd_hdac_power_up_pm(struct hdac_device *codec) +int snd_hdac_power_up_pm(struct hdac_device *codec) { if (!atomic_inc_not_zero(&codec->in_pm)) - snd_hdac_power_up(codec); + return snd_hdac_power_up(codec); + return 0; } EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); @@ -543,11 +580,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); * * Like snd_hdac_power_up_pm(), this function is used in a recursive * code path like init code which may be called by PM suspend/resume again. + * + * Returns zero if successful, or a negative error code. */ -void snd_hdac_power_down_pm(struct hdac_device *codec) +int snd_hdac_power_down_pm(struct hdac_device *codec) { if (atomic_dec_if_positive(&codec->in_pm) < 0) - snd_hdac_power_down(codec); + return snd_hdac_power_down(codec); + return 0; } EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); #endif diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 5676b84..55c3df4 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -134,6 +134,16 @@ static int hdac_component_master_match(struct device *dev, void *data) return !strcmp(dev->driver->name, "i915"); } +int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops) +{ + if (WARN_ON(!hdac_acomp)) + return -ENODEV; + + hdac_acomp->audio_ops = aops; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier); + int snd_hdac_i915_init(struct hdac_bus *bus) { struct component_match *match = NULL; diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 1eabcdf..b0ed870 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -410,8 +410,9 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, err = reg_raw_write(codec, reg, val); if (err == -EAGAIN) { - snd_hdac_power_up_pm(codec); - err = reg_raw_write(codec, reg, val); + err = snd_hdac_power_up_pm(codec); + if (!err) + err = reg_raw_write(codec, reg, val); snd_hdac_power_down_pm(codec); } return err; @@ -442,8 +443,9 @@ int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, err = reg_raw_read(codec, reg, val); if (err == -EAGAIN) { - snd_hdac_power_up_pm(codec); - err = reg_raw_read(codec, reg, val); + err = snd_hdac_power_up_pm(codec); + if (!err) + err = reg_raw_read(codec, reg, val); snd_hdac_power_down_pm(codec); } return err; diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 4c15d0a..8981159 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -286,6 +286,28 @@ void snd_hdac_stream_release(struct hdac_stream *azx_dev) } EXPORT_SYMBOL_GPL(snd_hdac_stream_release); +/** + * snd_hdac_get_stream - return hdac_stream based on stream_tag and + * direction + * + * @bus: HD-audio core bus + * @dir: direction for the stream to be found + * @stream_tag: stream tag for stream to be found + */ +struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus, + int dir, int stream_tag) +{ + struct hdac_stream *s; + + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == dir && s->stream_tag == stream_tag) + return s; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_stream); + /* * set up a BDL entry */ diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 0a6ce3b..c71142d 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -321,8 +321,7 @@ static void widget_tree_free(struct hdac_device *codec) free_widget_node(*p, &widget_node_group); kfree(tree->nodes); } - if (tree->root) - kobject_put(tree->root); + kobject_put(tree->root); kfree(tree); codec->widgets = NULL; } @@ -391,6 +390,9 @@ int hda_widget_sysfs_init(struct hdac_device *codec) { int err; + if (codec->widgets) + return 0; /* already created */ + err = widget_tree_create(codec); if (err < 0) { widget_tree_free(codec); diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index febee5b..320837b 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -44,18 +44,18 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_DARLA20_DSP; chip->spdif_status = GD_SPDIF_STATUS_UNDEF; chip->clock_state = GD_CLOCK_UNDEF; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index 7b4f6fd..8736b5e 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -44,17 +44,17 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_DARLA24_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_ESYNC; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index ae11ce1..6deb80c 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -59,8 +59,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2); chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; - chip->has_midi = TRUE; + chip->bad_board = true; + chip->has_midi = true; chip->dsp_code_to_load = FW_ECHO3G_DSP; /* Load the DSP code and the ASIC on the PCI card and get @@ -78,8 +78,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->px_analog_in = chip->bx_analog_in = 14; chip->px_digital_in = chip->bx_digital_in = 16; chip->px_num = chip->bx_num = 24; - chip->has_phantom_power = TRUE; - chip->hasnt_input_nominal_level = TRUE; + chip->has_phantom_power = true; + chip->hasnt_input_nominal_level = true; } else if (err == E3G_LAYLA3G_BOX_TYPE) { chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | @@ -106,10 +106,10 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) static int set_mixer_defaults(struct echoaudio *chip) { chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->non_audio_spdif = FALSE; - chip->bad_board = FALSE; - chip->phantom_power = FALSE; + chip->professional_spdif = false; + chip->non_audio_spdif = false; + chip->bad_board = false; + chip->phantom_power = false; return init_line_levels(chip); } diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 862db9a..1cb85ae 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2245,7 +2245,7 @@ static int snd_echo_resume(struct device *dev) #ifdef ECHOCARD_HAS_MIDI if (chip->midi_input_enabled) - enable_midi_input(chip, TRUE); + enable_midi_input(chip, true); if (chip->midi_out) snd_echo_midi_output_trigger(chip->midi_out, 1); #endif diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index 3251522..152ce15 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -153,9 +153,6 @@ #define _ECHOAUDIO_H_ -#define TRUE 1 -#define FALSE 0 - #include "echoaudio_dsp.h" @@ -378,8 +375,8 @@ struct echoaudio { */ u8 output_clock; /* Layla20 only */ char meters_enabled; /* VU-meters status */ - char asic_loaded; /* Set TRUE when ASIC loaded */ - char bad_board; /* Set TRUE if DSP won't load */ + char asic_loaded; /* Set true when ASIC loaded */ + char bad_board; /* Set true if DSP won't load */ char professional_spdif; /* 0 = consumer; 1 = professional */ char non_audio_spdif; /* 3G - only */ char digital_in_automute; /* Gina24, Layla24, Mona - only */ diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index 2fa66dc..22c786b 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -41,7 +41,7 @@ static int check_asic_status(struct echoaudio *chip) return -EIO; chip->comm_page->ext_box_status = cpu_to_le32(E3G_ASIC_NOT_LOADED); - chip->asic_loaded = FALSE; + chip->asic_loaded = false; clear_handshake(chip); send_vector(chip, DSP_VC_TEST_ASIC); @@ -55,7 +55,7 @@ static int check_asic_status(struct echoaudio *chip) if (box_status == E3G_ASIC_NOT_LOADED) return -ENODEV; - chip->asic_loaded = TRUE; + chip->asic_loaded = true; return box_status & E3G_BOX_TYPE_MASK; } @@ -243,7 +243,7 @@ static int load_asic(struct echoaudio *chip) * 48 kHz, internal clock, S/PDIF RCA mode */ if (box_type >= 0) { err = write_control_reg(chip, E3G_48KHZ, - E3G_FREQ_REG_DEFAULT, TRUE); + E3G_FREQ_REG_DEFAULT, true); if (err < 0) return err; } @@ -378,16 +378,16 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) int err, incompatible_clock; /* Set clock to "internal" if it's not compatible with the new mode */ - incompatible_clock = FALSE; + incompatible_clock = false; switch (mode) { case DIGITAL_MODE_SPDIF_OPTICAL: case DIGITAL_MODE_SPDIF_RCA: if (chip->input_clock == ECHO_CLOCK_ADAT) - incompatible_clock = TRUE; + incompatible_clock = true; break; case DIGITAL_MODE_ADAT: if (chip->input_clock == ECHO_CLOCK_SPDIF) - incompatible_clock = TRUE; + incompatible_clock = true; break; default: dev_err(chip->card->dev, diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 1a9427a..15aae2f 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -103,8 +103,8 @@ static int write_dsp(struct echoaudio *chip, u32 data) cond_resched(); } - chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ - dev_dbg(chip->card->dev, "write_dsp: Set bad_board to TRUE\n"); + chip->bad_board = true; /* Set true until DSP re-loaded */ + dev_dbg(chip->card->dev, "write_dsp: Set bad_board to true\n"); return -EIO; } @@ -126,8 +126,8 @@ static int read_dsp(struct echoaudio *chip, u32 *data) cond_resched(); } - chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ - dev_err(chip->card->dev, "read_dsp: Set bad_board to TRUE\n"); + chip->bad_board = true; /* Set true until DSP re-loaded */ + dev_err(chip->card->dev, "read_dsp: Set bad_board to true\n"); return -EIO; } @@ -166,7 +166,7 @@ static int read_sn(struct echoaudio *chip) /* This card has no ASIC, just return ok */ static inline int check_asic_status(struct echoaudio *chip) { - chip->asic_loaded = TRUE; + chip->asic_loaded = true; return 0; } @@ -341,11 +341,11 @@ static int load_dsp(struct echoaudio *chip, u16 *code) dev_warn(chip->card->dev, "DSP is already loaded!\n"); return 0; } - chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ + chip->bad_board = true; /* Set true until DSP loaded */ chip->dsp_code = NULL; /* Current DSP code not loaded */ - chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */ + chip->asic_loaded = false; /* Loading the DSP code will reset the ASIC */ - dev_dbg(chip->card->dev, "load_dsp: Set bad_board to TRUE\n"); + dev_dbg(chip->card->dev, "load_dsp: Set bad_board to true\n"); /* If this board requires a resident loader, install it. */ #ifdef DSP_56361 @@ -471,7 +471,7 @@ static int load_dsp(struct echoaudio *chip, u16 *code) } chip->dsp_code = code; /* Show which DSP code loaded */ - chip->bad_board = FALSE; /* DSP OK */ + chip->bad_board = false; /* DSP OK */ return 0; } udelay(100); @@ -951,10 +951,10 @@ static int rest_in_peace(struct echoaudio *chip) /* Stops all active pipes (just to be sure) */ stop_transport(chip, chip->active_mask); - set_meters_on(chip, FALSE); + set_meters_on(chip, false); #ifdef ECHOCARD_HAS_MIDI - enable_midi_input(chip, FALSE); + enable_midi_input(chip, false); #endif /* Go to sleep */ @@ -981,9 +981,9 @@ static int init_dsp_comm_page(struct echoaudio *chip) /* Init all the basic stuff */ chip->card_name = ECHOCARD_NAME; - chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ + chip->bad_board = true; /* Set true until DSP loaded */ chip->dsp_code = NULL; /* Current DSP code not loaded */ - chip->asic_loaded = FALSE; + chip->asic_loaded = false; memset(chip->comm_page, 0, sizeof(struct comm_page)); /* Init the comm page */ diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c index 23a0994..834b39e 100644 --- a/sound/pci/echoaudio/echoaudio_gml.c +++ b/sound/pci/echoaudio/echoaudio_gml.c @@ -48,7 +48,7 @@ static int check_asic_status(struct echoaudio *chip) if (read_dsp(chip, &asic_status) < 0) { dev_err(chip->card->dev, "check_asic_status: failed on read_dsp\n"); - chip->asic_loaded = FALSE; + chip->asic_loaded = false; return -EIO; } @@ -192,7 +192,7 @@ static int set_professional_spdif(struct echoaudio *chip, char prof) } } - if ((err = write_control_reg(chip, control_reg, FALSE))) + if ((err = write_control_reg(chip, control_reg, false))) return err; chip->professional_spdif = prof; dev_dbg(chip->card->dev, "set_professional_spdif to %s\n", diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index 4fa32a2..67bd0c9 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -23,7 +23,7 @@ #define ECHOCARD_HAS_INPUT_GAIN #define ECHOCARD_HAS_DIGITAL_IO #define ECHOCARD_HAS_EXTERNAL_CLOCK -#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_ADAT false /* Pipe indexes */ #define PX_ANALOG_OUT 0 /* 8 */ diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index 5dafe92..b237757 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -48,19 +48,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_GINA20_DSP; chip->spdif_status = GD_SPDIF_STATUS_UNDEF; chip->clock_state = GD_CLOCK_UNDEF; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } @@ -69,7 +69,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) static int set_mixer_defaults(struct echoaudio *chip) { - chip->professional_spdif = FALSE; + chip->professional_spdif = false; return init_line_levels(chip); } diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index 6971766..8eff2b4 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -52,7 +52,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 | @@ -76,7 +76,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } @@ -86,8 +86,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) static int set_mixer_defaults(struct echoaudio *chip) { chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->digital_in_automute = TRUE; + chip->professional_spdif = false; + chip->digital_in_automute = true; return init_line_levels(chip); } @@ -152,7 +152,7 @@ static int load_asic(struct echoaudio *chip) 48 kHz, internal clock, S/PDIF RCA mode */ if (!err) { control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; - err = write_control_reg(chip, control_reg, TRUE); + err = write_control_reg(chip, control_reg, true); } return err; } @@ -226,7 +226,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->sample_rate = rate; dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, clock); - return write_control_reg(chip, control_reg, FALSE); + return write_control_reg(chip, control_reg, false); } @@ -274,7 +274,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) } chip->input_clock = clock; - return write_control_reg(chip, control_reg, TRUE); + return write_control_reg(chip, control_reg, true); } @@ -285,17 +285,17 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) int err, incompatible_clock; /* Set clock to "internal" if it's not compatible with the new mode */ - incompatible_clock = FALSE; + incompatible_clock = false; switch (mode) { case DIGITAL_MODE_SPDIF_OPTICAL: case DIGITAL_MODE_SPDIF_CDROM: case DIGITAL_MODE_SPDIF_RCA: if (chip->input_clock == ECHO_CLOCK_ADAT) - incompatible_clock = TRUE; + incompatible_clock = true; break; case DIGITAL_MODE_ADAT: if (chip->input_clock == ECHO_CLOCK_SPDIF) - incompatible_clock = TRUE; + incompatible_clock = true; break; default: dev_err(chip->card->dev, @@ -333,7 +333,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) break; } - err = write_control_reg(chip, control_reg, TRUE); + err = write_control_reg(chip, control_reg, true); spin_unlock_irq(&chip->lock); if (err < 0) return err; diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index 54edd67..c97dc83 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -49,16 +49,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_INDIGO_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index 2cf5cc0..2428b35 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -49,16 +49,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_INDIGO_DJ_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c index 5252863..5fbd4a3 100644 --- a/sound/pci/echoaudio/indigodjx_dsp.c +++ b/sound/pci/echoaudio/indigodjx_dsp.c @@ -47,17 +47,17 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_INDIGO_DJX_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; err = load_firmware(chip); if (err < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index 4e81787..79b68ba 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -49,16 +49,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_INDIGO_IO_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c index 6de3f9b..1ae394e 100644 --- a/sound/pci/echoaudio/indigoiox_dsp.c +++ b/sound/pci/echoaudio/indigoiox_dsp.c @@ -47,17 +47,17 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_INDIGO_IOX_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; err = load_firmware(chip); if (err < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index 12e5d21..fc8468d 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -26,7 +26,7 @@ #define ECHOCARD_HAS_SUPER_INTERLEAVE #define ECHOCARD_HAS_DIGITAL_IO #define ECHOCARD_HAS_EXTERNAL_CLOCK -#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_ADAT false #define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH #define ECHOCARD_HAS_MIDI diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index f2024a3..5e5b6e2 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -51,8 +51,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; - chip->has_midi = TRUE; + chip->bad_board = true; + chip->has_midi = true; chip->dsp_code_to_load = FW_LAYLA20_DSP; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | @@ -62,7 +62,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } @@ -71,7 +71,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) static int set_mixer_defaults(struct echoaudio *chip) { - chip->professional_spdif = FALSE; + chip->professional_spdif = false; return init_line_levels(chip); } @@ -113,7 +113,7 @@ static int check_asic_status(struct echoaudio *chip) u32 asic_status; int goodcnt, i; - chip->asic_loaded = FALSE; + chip->asic_loaded = false; for (i = goodcnt = 0; i < 5; i++) { send_vector(chip, DSP_VC_TEST_ASIC); @@ -127,7 +127,7 @@ static int check_asic_status(struct echoaudio *chip) if (asic_status == ASIC_ALREADY_LOADED) { if (++goodcnt == 3) { - chip->asic_loaded = TRUE; + chip->asic_loaded = true; return 0; } } diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index 4f11e81..df28e51 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -51,8 +51,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; - chip->has_midi = TRUE; + chip->bad_board = true; + chip->has_midi = true; chip->dsp_code_to_load = FW_LAYLA24_DSP; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | @@ -64,7 +64,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; if ((err = init_line_levels(chip)) < 0) return err; @@ -77,8 +77,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) static int set_mixer_defaults(struct echoaudio *chip) { chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->digital_in_automute = TRUE; + chip->professional_spdif = false; + chip->digital_in_automute = true; return init_line_levels(chip); } @@ -135,7 +135,7 @@ static int load_asic(struct echoaudio *chip) err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, FW_LAYLA24_2S_ASIC); if (err < 0) - return FALSE; + return false; /* Now give the external ASIC a little time to set up */ mdelay(10); @@ -147,7 +147,7 @@ static int load_asic(struct echoaudio *chip) 48 kHz, internal clock, S/PDIF RCA mode */ if (!err) err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ, - TRUE); + true); return err; } @@ -241,7 +241,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, control_reg); - return write_control_reg(chip, control_reg, FALSE); + return write_control_reg(chip, control_reg, false); } @@ -287,7 +287,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) } chip->input_clock = clock; - return write_control_reg(chip, control_reg, TRUE); + return write_control_reg(chip, control_reg, true); } @@ -334,17 +334,17 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) short asic; /* Set clock to "internal" if it's not compatible with the new mode */ - incompatible_clock = FALSE; + incompatible_clock = false; switch (mode) { case DIGITAL_MODE_SPDIF_OPTICAL: case DIGITAL_MODE_SPDIF_RCA: if (chip->input_clock == ECHO_CLOCK_ADAT) - incompatible_clock = TRUE; + incompatible_clock = true; asic = FW_LAYLA24_2S_ASIC; break; case DIGITAL_MODE_ADAT: if (chip->input_clock == ECHO_CLOCK_SPDIF) - incompatible_clock = TRUE; + incompatible_clock = true; asic = FW_LAYLA24_2A_ASIC; break; default: @@ -383,7 +383,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) break; } - err = write_control_reg(chip, control_reg, TRUE); + err = write_control_reg(chip, control_reg, true); spin_unlock_irq(&chip->lock); if (err < 0) return err; diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index 2f7562f..62b5240 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -26,7 +26,7 @@ #define ECHOCARD_HAS_VMIXER #define ECHOCARD_HAS_DIGITAL_IO #define ECHOCARD_HAS_EXTERNAL_CLOCK -#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_ADAT false #define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 #define ECHOCARD_HAS_MIDI #define ECHOCARD_HAS_LINE_OUT_GAIN diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index fdad079..8f612a0 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -52,19 +52,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->dsp_code_to_load = FW_MIA_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ - chip->asic_loaded = TRUE; + chip->asic_loaded = true; if ((subdevice_id & 0x0000f) == MIA_MIDI_REV) - chip->has_midi = TRUE; + chip->has_midi = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF; if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index 843c7a5..dce9e57 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -52,7 +52,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; - chip->bad_board = TRUE; + chip->bad_board = true; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT; @@ -69,7 +69,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = load_firmware(chip)) < 0) return err; - chip->bad_board = FALSE; + chip->bad_board = false; return err; } @@ -79,8 +79,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) static int set_mixer_defaults(struct echoaudio *chip) { chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->digital_in_automute = TRUE; + chip->professional_spdif = false; + chip->digital_in_automute = true; return init_line_levels(chip); } @@ -148,7 +148,7 @@ static int load_asic(struct echoaudio *chip) 48 kHz, internal clock, S/PDIF RCA mode */ if (!err) { control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; - err = write_control_reg(chip, control_reg, TRUE); + err = write_control_reg(chip, control_reg, true); } return err; @@ -356,7 +356,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) } chip->input_clock = clock; - return write_control_reg(chip, control_reg, TRUE); + return write_control_reg(chip, control_reg, true); } @@ -367,16 +367,16 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) int err, incompatible_clock; /* Set clock to "internal" if it's not compatible with the new mode */ - incompatible_clock = FALSE; + incompatible_clock = false; switch (mode) { case DIGITAL_MODE_SPDIF_OPTICAL: case DIGITAL_MODE_SPDIF_RCA: if (chip->input_clock == ECHO_CLOCK_ADAT) - incompatible_clock = TRUE; + incompatible_clock = true; break; case DIGITAL_MODE_ADAT: if (chip->input_clock == ECHO_CLOCK_SPDIF) - incompatible_clock = TRUE; + incompatible_clock = true; break; default: dev_err(chip->card->dev, @@ -415,7 +415,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) break; } - err = write_control_reg(chip, control_reg, FALSE); + err = write_control_reg(chip, control_reg, false); spin_unlock_irq(&chip->lock); if (err < 0) return err; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 55e5716..076b117 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1741,7 +1741,7 @@ static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new snd_audigy_capture_boost = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Capture Boost", + .name = "Mic Extra Boost", .info = snd_audigy_capture_boost_info, .get = snd_audigy_capture_boost_get, .put = snd_audigy_capture_boost_put @@ -1819,8 +1819,6 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, * the Philips ADC for 24bit capture */ "PCM Playback Switch", "PCM Playback Volume", - "Master Mono Playback Switch", - "Master Mono Playback Volume", "Master Playback Switch", "Master Playback Volume", "PCM Out Path & Mute", @@ -1830,10 +1828,16 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Capture Switch", "Capture Volume", "Mic Select", + "Headphone Playback Switch", + "Headphone Playback Volume", + "3D Control - Center", + "3D Control - Depth", + "3D Control - Switch", "Video Playback Switch", "Video Playback Volume", "Mic Playback Switch", "Mic Playback Volume", + "External Amplifier", NULL }; static char *audigy_rename_ctls[] = { @@ -1842,6 +1846,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, /* "Wave Capture Volume", "PCM Capture Volume", */ "Wave Master Playback Volume", "Master Playback Volume", "AMic Playback Volume", "Mic Playback Volume", + "Master Mono Playback Switch", "Phone Output Playback Switch", + "Master Mono Playback Volume", "Phone Output Playback Volume", NULL }; static char *audigy_rename_ctls_i2c_adc[] = { @@ -1867,8 +1873,6 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, * the Philips ADC for 24bit capture */ "PCM Playback Switch", "PCM Playback Volume", - "Master Mono Playback Switch", - "Master Mono Playback Volume", "Capture Source", "Capture Switch", "Capture Volume", @@ -1900,7 +1904,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Aux Playback Volume", "Aux Capture Volume", "Video Playback Switch", "Video Capture Switch", "Video Playback Volume", "Video Capture Volume", - + "Master Mono Playback Switch", "Phone Output Playback Switch", + "Master Mono Playback Volume", "Phone Output Playback Volume", NULL }; @@ -1935,6 +1940,9 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000); /* set capture source to mic */ snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000); + /* set mono output (TAD) to mic */ + snd_ac97_update_bits(emu->ac97, AC97_GENERAL_PURPOSE, + 0x0200, 0x0200); if (emu->card_capabilities->adc_1361t) c = audigy_remove_ctls_1361t_adc; else @@ -1996,11 +2004,6 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume"); rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume"); rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume"); - remove_ctl(card, "Headphone Playback Switch"); - remove_ctl(card, "Headphone Playback Volume"); - remove_ctl(card, "3D Control - Center"); - remove_ctl(card, "3D Control - Depth"); - remove_ctl(card, "3D Control - Switch"); } if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL) return -ENOMEM; diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 03b7399..7f57a14 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -887,11 +887,32 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup); static bool pin_config_match(struct hda_codec *codec, const struct hda_pintbl *pins) { - for (; pins->nid; pins++) { - u32 def_conf = snd_hda_codec_get_pincfg(codec, pins->nid); - if (pins->val != def_conf) + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + hda_nid_t nid = pin->nid; + u32 cfg = pin->cfg; + const struct hda_pintbl *t_pins; + int found; + + t_pins = pins; + found = 0; + for (; t_pins->nid; t_pins++) { + if (t_pins->nid == nid) { + found = 1; + if (t_pins->val == cfg) + break; + else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000) + break; + else + return false; + } + } + if (!found && (cfg & 0xf0000000) != 0x40000000) return false; } + return true; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d1a2cb6..37f43a1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -53,76 +53,6 @@ #define codec_has_clkstop(codec) \ ((codec)->core.power_caps & AC_PWRST_CLKSTOP) -/** - * snd_hda_get_jack_location - Give a location string of the jack - * @cfg: pin default config value - * - * Parse the pin default config value and returns the string of the - * jack location, e.g. "Rear", "Front", etc. - */ -const char *snd_hda_get_jack_location(u32 cfg) -{ - static char *bases[7] = { - "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", - }; - static unsigned char specials_idx[] = { - 0x07, 0x08, - 0x17, 0x18, 0x19, - 0x37, 0x38 - }; - static char *specials[] = { - "Rear Panel", "Drive Bar", - "Riser", "HDMI", "ATAPI", - "Mobile-In", "Mobile-Out" - }; - int i; - cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; - if ((cfg & 0x0f) < 7) - return bases[cfg & 0x0f]; - for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { - if (cfg == specials_idx[i]) - return specials[i]; - } - return "UNKNOWN"; -} -EXPORT_SYMBOL_GPL(snd_hda_get_jack_location); - -/** - * snd_hda_get_jack_connectivity - Give a connectivity string of the jack - * @cfg: pin default config value - * - * Parse the pin default config value and returns the string of the - * jack connectivity, i.e. external or internal connection. - */ -const char *snd_hda_get_jack_connectivity(u32 cfg) -{ - static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; - - return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; -} -EXPORT_SYMBOL_GPL(snd_hda_get_jack_connectivity); - -/** - * snd_hda_get_jack_type - Give a type string of the jack - * @cfg: pin default config value - * - * Parse the pin default config value and returns the string of the - * jack type, i.e. the purpose of the jack, such as Line-Out or CD. - */ -const char *snd_hda_get_jack_type(u32 cfg) -{ - static char *jack_types[16] = { - "Line Out", "Speaker", "HP Out", "CD", - "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", - "Line In", "Aux", "Mic", "Telephony", - "SPDIF In", "Digital In", "Reserved", "Other" - }; - - return jack_types[(cfg & AC_DEFCFG_DEVICE) - >> AC_DEFCFG_DEVICE_SHIFT]; -} -EXPORT_SYMBOL_GPL(snd_hda_get_jack_type); - /* * Send and receive a verb - passed to exec_verb override for hdac_device */ @@ -975,7 +905,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (codec->bus->modelname) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { - err = -ENODEV; + err = -ENOMEM; goto error; } } @@ -1025,7 +955,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; - err = snd_hdac_refresh_widgets(&codec->core); + err = snd_hdac_refresh_widget_sysfs(&codec->core); if (err < 0) return err; @@ -3172,8 +3102,7 @@ static int add_std_chmaps(struct hda_codec *codec) struct snd_pcm_chmap *chmap; const struct snd_pcm_chmap_elem *elem; - if (!pcm || !pcm->pcm || pcm->own_chmap || - !hinfo->substreams) + if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams) continue; elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem, diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 12837ab..2970413 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -469,13 +469,6 @@ int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) } /* - * get widget information - */ -const char *snd_hda_get_jack_connectivity(u32 cfg); -const char *snd_hda_get_jack_type(u32 cfg); -const char *snd_hda_get_jack_location(u32 cfg); - -/* * power saving */ #define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core) diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index c746cd9..563984d 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -42,7 +42,7 @@ enum cea_edid_versions { CEA_EDID_VER_RESERVED = 4, }; -static char *cea_speaker_allocation_names[] = { +static const char * const cea_speaker_allocation_names[] = { /* 0 */ "FL/FR", /* 1 */ "LFE", /* 2 */ "FC", @@ -56,7 +56,7 @@ static char *cea_speaker_allocation_names[] = { /* 10 */ "FCH", }; -static char *eld_connection_type_names[4] = { +static const char * const eld_connection_type_names[4] = { "HDMI", "DisplayPort", "2-reserved", @@ -94,7 +94,7 @@ enum cea_audio_coding_xtypes { AUDIO_CODING_XTYPE_FIRST_RESERVED = 4, }; -static char *cea_audio_coding_type_names[] = { +static const char * const cea_audio_coding_type_names[] = { /* 0 */ "undefined", /* 1 */ "LPCM", /* 2 */ "AC-3", @@ -482,14 +482,14 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld, struct parsed_hdmi_eld *e = &eld->info; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; int i; - static char *eld_version_names[32] = { + static const char * const eld_version_names[32] = { "reserved", "reserved", "CEA-861D or below", [3 ... 30] = "reserved", [31] = "partial" }; - static char *cea_edid_version_names[8] = { + static const char * const cea_edid_version_names[8] = { "no CEA EDID Timing Extension block present", "CEA-861", "CEA-861-A", diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index baaf7ed0..033aa84 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -36,24 +36,9 @@ MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1 #define param_read(codec, nid, parm) \ snd_hdac_read_parm_uncached(&(codec)->core, nid, parm) -static char *bits_names(unsigned int bits, char *names[], int size) -{ - int i, n; - static char buf[128]; - - for (i = 0, n = 0; i < size; i++) { - if (bits & (1U<<i) && names[i]) - n += snprintf(buf + n, sizeof(buf) - n, " %s", - names[i]); - } - buf[n] = '\0'; - - return buf; -} - static const char *get_wid_type_name(unsigned int wid_value) { - static char *names[16] = { + static const char * const names[16] = { [AC_WID_AUD_OUT] = "Audio Output", [AC_WID_AUD_IN] = "Audio Input", [AC_WID_AUD_MIX] = "Audio Mixer", @@ -241,7 +226,7 @@ static void print_pcm_caps(struct snd_info_buffer *buffer, static const char *get_jack_connection(u32 cfg) { - static char *names[16] = { + static const char * const names[16] = { "Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical","Digital", "Analog", "DIN", "XLR", "RJ11", "Comb", @@ -256,7 +241,7 @@ static const char *get_jack_connection(u32 cfg) static const char *get_jack_color(u32 cfg) { - static char *names[16] = { + static const char * const names[16] = { "Unknown", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow", "Purple", "Pink", NULL, NULL, @@ -269,11 +254,74 @@ static const char *get_jack_color(u32 cfg) return "UNKNOWN"; } +/* + * Parse the pin default config value and returns the string of the + * jack location, e.g. "Rear", "Front", etc. + */ +static const char *get_jack_location(u32 cfg) +{ + static const char * const bases[7] = { + "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", + }; + static const unsigned char specials_idx[] = { + 0x07, 0x08, + 0x17, 0x18, 0x19, + 0x37, 0x38 + }; + static const char * const specials[] = { + "Rear Panel", "Drive Bar", + "Riser", "HDMI", "ATAPI", + "Mobile-In", "Mobile-Out" + }; + int i; + + cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; + if ((cfg & 0x0f) < 7) + return bases[cfg & 0x0f]; + for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { + if (cfg == specials_idx[i]) + return specials[i]; + } + return "UNKNOWN"; +} + +/* + * Parse the pin default config value and returns the string of the + * jack connectivity, i.e. external or internal connection. + */ +static const char *get_jack_connectivity(u32 cfg) +{ + static const char * const jack_locations[4] = { + "Ext", "Int", "Sep", "Oth" + }; + + return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; +} + +/* + * Parse the pin default config value and returns the string of the + * jack type, i.e. the purpose of the jack, such as Line-Out or CD. + */ +static const char *get_jack_type(u32 cfg) +{ + static const char * const jack_types[16] = { + "Line Out", "Speaker", "HP Out", "CD", + "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", + "Line In", "Aux", "Mic", "Telephony", + "SPDIF In", "Digital In", "Reserved", "Other" + }; + + return jack_types[(cfg & AC_DEFCFG_DEVICE) + >> AC_DEFCFG_DEVICE_SHIFT]; +} + static void print_pin_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int *supports_vref) { - static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; + static const char * const jack_conns[4] = { + "Jack", "N/A", "Fixed", "Both" + }; unsigned int caps, val; caps = param_read(codec, nid, AC_PAR_PIN_CAP); @@ -340,9 +388,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer, caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], - snd_hda_get_jack_type(caps), - snd_hda_get_jack_connectivity(caps), - snd_hda_get_jack_location(caps)); + get_jack_type(caps), + get_jack_connectivity(caps), + get_jack_location(caps)); snd_iprintf(buffer, " Conn = %s, Color = %s\n", get_jack_connection(caps), get_jack_color(caps)); @@ -478,7 +526,7 @@ static const char *get_pwr_state(u32 state) static void print_power_state(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - static char *names[] = { + static const char * const names[] = { [ilog2(AC_PWRST_D0SUP)] = "D0", [ilog2(AC_PWRST_D1SUP)] = "D1", [ilog2(AC_PWRST_D2SUP)] = "D2", @@ -492,9 +540,16 @@ static void print_power_state(struct snd_info_buffer *buffer, int sup = param_read(codec, nid, AC_PAR_POWER_STATE); int pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); - if (sup != -1) - snd_iprintf(buffer, " Power states: %s\n", - bits_names(sup, names, ARRAY_SIZE(names))); + if (sup != -1) { + int i; + + snd_iprintf(buffer, " Power states: "); + for (i = 0; i < ARRAY_SIZE(names); i++) { + if (sup & (1U << i)) + snd_iprintf(buffer, " %s", names[i]); + } + snd_iprintf(buffer, "\n"); + } snd_iprintf(buffer, " Power: setting=%s, actual=%s", get_pwr_state(pwr & AC_PWRST_SETTING), diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0f039ab..186792f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -763,6 +763,20 @@ enum { QUIRK_ALIENWARE, }; +static const struct hda_pintbl alienware_pincfgs[] = { + { 0x0b, 0x90170110 }, /* Builtin Speaker */ + { 0x0c, 0x411111f0 }, /* N/A */ + { 0x0d, 0x411111f0 }, /* N/A */ + { 0x0e, 0x411111f0 }, /* N/A */ + { 0x0f, 0x0321101f }, /* HP */ + { 0x10, 0x411111f0 }, /* Headset? disabled for now */ + { 0x11, 0x03a11021 }, /* Mic */ + { 0x12, 0xd5a30140 }, /* Builtin Mic */ + { 0x13, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + {} +}; + static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15", QUIRK_ALIENWARE), {} @@ -3147,7 +3161,7 @@ static int ca0132_select_out(struct hda_codec *codec) auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; if (auto_jack) - jack_present = snd_hda_jack_detect(codec, spec->out_pins[1]); + jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp); else jack_present = spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID]; @@ -3309,7 +3323,7 @@ static int ca0132_select_mic(struct hda_codec *codec) auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; if (auto_jack) - jack_present = snd_hda_jack_detect(codec, spec->input_pins[0]); + jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_amic1); else jack_present = spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID]; @@ -4617,37 +4631,54 @@ static void ca0132_config(struct hda_codec *codec) spec->multiout.num_dacs = 3; spec->multiout.max_channels = 2; - spec->num_outputs = 2; - spec->out_pins[0] = 0x0b; /* speaker out */ if (spec->quirk == QUIRK_ALIENWARE) { codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n"); + snd_hda_apply_pincfgs(codec, alienware_pincfgs); + + spec->num_outputs = 2; + spec->out_pins[0] = 0x0b; /* speaker out */ spec->out_pins[1] = 0x0f; - } else{ + spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = 0x0f; + + spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ + spec->adcs[1] = 0x8; /* analog mic2 */ + spec->adcs[2] = 0xa; /* what u hear */ + + spec->num_inputs = 3; + spec->input_pins[0] = 0x12; + spec->input_pins[1] = 0x11; + spec->input_pins[2] = 0x13; + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = 0x11; + } else { + spec->num_outputs = 2; + spec->out_pins[0] = 0x0b; /* speaker out */ spec->out_pins[1] = 0x10; /* headphone out */ + spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = spec->out_pins[1]; + + spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ + spec->adcs[1] = 0x8; /* analog mic2 */ + spec->adcs[2] = 0xa; /* what u hear */ + + spec->num_inputs = 3; + spec->input_pins[0] = 0x12; + spec->input_pins[1] = 0x11; + spec->input_pins[2] = 0x13; + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = spec->input_pins[0]; + + /* SPDIF I/O */ + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + cfg->dig_out_pins[0] = 0x0c; + cfg->dig_outs = 1; + cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF; + spec->dig_in = 0x09; + cfg->dig_in_pin = 0x0e; + cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; } - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = spec->out_pins[1]; - - spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ - spec->adcs[1] = 0x8; /* analog mic2 */ - spec->adcs[2] = 0xa; /* what u hear */ - - spec->num_inputs = 3; - spec->input_pins[0] = 0x12; - spec->input_pins[1] = 0x11; - spec->input_pins[2] = 0x13; - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = spec->input_pins[0]; - - /* SPDIF I/O */ - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - cfg->dig_out_pins[0] = 0x0c; - cfg->dig_outs = 1; - cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF; - spec->dig_in = 0x09; - cfg->dig_in_pin = 0x0e; - cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; } static int ca0132_prepare_verbs(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index a97db5f..acbfbe08 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -37,6 +37,8 @@ #include <sound/jack.h> #include <sound/asoundef.h> #include <sound/tlv.h> +#include <sound/hdaudio.h> +#include <sound/hda_i915.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -144,6 +146,9 @@ struct hdmi_spec { */ struct hda_multi_out multiout; struct hda_pcm_stream pcm_playback; + + /* i915/powerwell (Haswell+/Valleyview+) specific */ + struct i915_audio_component_audio_ops i915_audio_ops; }; @@ -2191,6 +2196,9 @@ static void generic_hdmi_free(struct hda_codec *codec) struct hdmi_spec *spec = codec->spec; int pin_idx; + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) + snd_hdac_i915_register_notifier(NULL); + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -2316,6 +2324,14 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_set_power_to_all(codec, fg, power_state); } +static void intel_pin_eld_notify(void *audio_ptr, int port) +{ + struct hda_codec *codec = audio_ptr; + int pin_nid = port + 0x04; + + check_presence_and_report(codec, pin_nid); +} + static int patch_generic_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -2342,8 +2358,12 @@ static int patch_generic_hdmi(struct hda_codec *codec) if (is_valleyview_plus(codec) || is_skylake(codec)) codec->core.link_power_control = 1; - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { codec->depop_delay = 0; + spec->i915_audio_ops.audio_ptr = codec; + spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; + snd_hdac_i915_register_notifier(&spec->i915_audio_ops); + } if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 374ea53..4e6b090 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5389,400 +5389,202 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {} }; -#define ALC255_STANDARD_PINS \ - {0x18, 0x411111f0}, \ - {0x19, 0x411111f0}, \ - {0x1a, 0x411111f0}, \ - {0x1b, 0x411111f0}, \ - {0x1e, 0x411111f0} - #define ALC256_STANDARD_PINS \ {0x12, 0x90a60140}, \ {0x14, 0x90170110}, \ - {0x19, 0x411111f0}, \ - {0x1a, 0x411111f0}, \ - {0x1b, 0x411111f0}, \ {0x21, 0x02211020} #define ALC282_STANDARD_PINS \ - {0x14, 0x90170110}, \ - {0x18, 0x411111f0}, \ - {0x1a, 0x411111f0}, \ - {0x1b, 0x411111f0}, \ - {0x1e, 0x411111f0} - -#define ALC288_STANDARD_PINS \ - {0x17, 0x411111f0}, \ - {0x18, 0x411111f0}, \ - {0x19, 0x411111f0}, \ - {0x1a, 0x411111f0}, \ - {0x1e, 0x411111f0} + {0x14, 0x90170110} #define ALC290_STANDARD_PINS \ - {0x12, 0x99a30130}, \ - {0x13, 0x40000000}, \ - {0x16, 0x411111f0}, \ - {0x17, 0x411111f0}, \ - {0x19, 0x411111f0}, \ - {0x1b, 0x411111f0}, \ - {0x1e, 0x411111f0} + {0x12, 0x99a30130} #define ALC292_STANDARD_PINS \ {0x14, 0x90170110}, \ - {0x15, 0x0221401f}, \ - {0x1a, 0x411111f0}, \ - {0x1b, 0x411111f0}, \ - {0x1d, 0x40700001} + {0x15, 0x0221401f} #define ALC298_STANDARD_PINS \ - {0x18, 0x411111f0}, \ - {0x19, 0x411111f0}, \ - {0x1a, 0x411111f0}, \ - {0x1e, 0x411111f0}, \ - {0x1f, 0x411111f0} + {0x12, 0x90a60130}, \ + {0x21, 0x03211020} static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, - {0x12, 0x40300000}, {0x14, 0x90170110}, - {0x17, 0x411111f0}, - {0x1d, 0x40538029}, {0x21, 0x02211020}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60140}, {0x14, 0x90170110}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211020}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170120}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x40000000}, {0x14, 0x90170130}, - {0x17, 0x411111f0}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, {0x1b, 0x01014020}, - {0x1d, 0x4054c029}, - {0x1e, 0x411111f0}, {0x21, 0x0221103f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x40000000}, {0x14, 0x90170150}, - {0x17, 0x411111f0}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, {0x1b, 0x02011020}, - {0x1d, 0x4054c029}, - {0x1e, 0x411111f0}, {0x21, 0x0221105f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x40000000}, {0x14, 0x90170110}, - {0x17, 0x411111f0}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, {0x1b, 0x01014020}, - {0x1d, 0x4054c029}, - {0x1e, 0x411111f0}, {0x21, 0x0221101f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, {0x14, 0x90170120}, {0x17, 0x90170140}, - {0x18, 0x40000000}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x41163b05}, - {0x1e, 0x411111f0}, {0x21, 0x0321102f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170130}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211040}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170140}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211050}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60170}, {0x14, 0x90170120}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60170}, {0x14, 0x90170130}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211040}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60170}, {0x14, 0x90170140}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211050}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60180}, {0x14, 0x90170130}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211040}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170120}, - {0x17, 0x40000000}, - {0x1d, 0x40700001}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC256_STANDARD_PINS, - {0x13, 0x40000000}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC256_STANDARD_PINS, - {0x13, 0x411111f0}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC256_STANDARD_PINS, - {0x13, 0x411111f0}, - {0x1d, 0x4077992d}, - {0x1e, 0x411111ff}), + ALC256_STANDARD_PINS), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, {0x12, 0x90a60130}, - {0x13, 0x40000000}, {0x14, 0x90170110}, {0x15, 0x0421101f}, - {0x16, 0x411111f0}, - {0x17, 0x411111f0}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x04a11020}, - {0x1b, 0x411111f0}, - {0x1d, 0x40748605}, - {0x1e, 0x411111f0}), + {0x1a, 0x04a11020}), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED, {0x12, 0x90a60140}, - {0x13, 0x40000000}, {0x14, 0x90170110}, {0x15, 0x0421101f}, - {0x16, 0x411111f0}, - {0x17, 0x411111f0}, {0x18, 0x02811030}, - {0x19, 0x411111f0}, {0x1a, 0x04a1103f}, - {0x1b, 0x02011020}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}), + {0x1b, 0x02011020}), SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC282_STANDARD_PINS, {0x12, 0x99a30130}, - {0x17, 0x40000000}, {0x19, 0x03a11020}, - {0x1d, 0x40f41905}, {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC282_STANDARD_PINS, {0x12, 0x99a30130}, - {0x17, 0x40020008}, {0x19, 0x03a11020}, - {0x1d, 0x40e00001}, {0x21, 0x03211040}), SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC282_STANDARD_PINS, {0x12, 0x99a30130}, - {0x17, 0x40000000}, {0x19, 0x03a11030}, - {0x1d, 0x40e00001}, {0x21, 0x03211020}), SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC282_STANDARD_PINS, {0x12, 0x99a30130}, - {0x17, 0x40000000}, - {0x19, 0x03a11030}, - {0x1d, 0x40f00001}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x17, 0x40000000}, {0x19, 0x04a11020}, - {0x1d, 0x40f00001}, {0x21, 0x0421101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x17, 0x40000000}, - {0x19, 0x03a11030}, - {0x1d, 0x40f00001}, - {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED, ALC282_STANDARD_PINS, {0x12, 0x90a60140}, - {0x17, 0x40000000}, {0x19, 0x04a11030}, - {0x1d, 0x40f00001}, {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC282_STANDARD_PINS, {0x12, 0x90a60130}, - {0x17, 0x40020008}, - {0x19, 0x411111f0}, - {0x1d, 0x40e00001}, {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, {0x14, 0x90170120}, - {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC282_STANDARD_PINS, {0x12, 0x90a60130}, - {0x17, 0x40020008}, {0x19, 0x03a11020}, - {0x1d, 0x40e00001}, {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6, - ALC288_STANDARD_PINS, {0x12, 0x90a60120}, - {0x13, 0x40000000}, {0x14, 0x90170110}, - {0x1d, 0x4076832d}, {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, - {0x14, 0x411111f0}, {0x15, 0x04211040}, {0x18, 0x90170112}, - {0x1a, 0x04a11020}, - {0x1d, 0x4075812d}), + {0x1a, 0x04a11020}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, - {0x14, 0x411111f0}, {0x15, 0x04211040}, {0x18, 0x90170110}, - {0x1a, 0x04a11020}, - {0x1d, 0x4075812d}), + {0x1a, 0x04a11020}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, - {0x14, 0x411111f0}, {0x15, 0x0421101f}, - {0x18, 0x411111f0}, - {0x1a, 0x04a11020}, - {0x1d, 0x4075812d}), + {0x1a, 0x04a11020}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, - {0x14, 0x411111f0}, {0x15, 0x04211020}, - {0x18, 0x411111f0}, - {0x1a, 0x04a11040}, - {0x1d, 0x4076a12d}), + {0x1a, 0x04a11040}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, {0x14, 0x90170110}, {0x15, 0x04211020}, - {0x18, 0x411111f0}, - {0x1a, 0x04a11040}, - {0x1d, 0x4076a12d}), + {0x1a, 0x04a11040}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, {0x14, 0x90170110}, {0x15, 0x04211020}, - {0x18, 0x411111f0}, - {0x1a, 0x04a11020}, - {0x1d, 0x4076a12d}), + {0x1a, 0x04a11020}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, {0x14, 0x90170110}, {0x15, 0x0421101f}, - {0x18, 0x411111f0}, - {0x1a, 0x04a11020}, - {0x1d, 0x4075812d}), + {0x1a, 0x04a11020}), SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x12, 0x90a60140}, - {0x13, 0x411111f0}, {0x16, 0x01014020}, - {0x18, 0x411111f0}, - {0x19, 0x01a19030}, - {0x1e, 0x411111f0}), + {0x19, 0x01a19030}), SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x12, 0x90a60140}, - {0x13, 0x411111f0}, {0x16, 0x01014020}, {0x18, 0x02a19031}, - {0x19, 0x01a1903e}, - {0x1e, 0x411111f0}), + {0x19, 0x01a1903e}), SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x13, 0x411111f0}, - {0x16, 0x411111f0}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1e, 0x411111f0}), + {0x12, 0x90a60140}), SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, - {0x12, 0x40000000}, {0x13, 0x90a60140}, {0x16, 0x21014020}, - {0x18, 0x411111f0}, - {0x19, 0x21a19030}, - {0x1e, 0x411111f0}), - SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x40000000}, - {0x13, 0x90a60140}, - {0x16, 0x411111f0}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1e, 0x411111f0}), + {0x19, 0x21a19030}), SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, - {0x12, 0x40000000}, - {0x13, 0x90a60140}, - {0x16, 0x21014020}, - {0x18, 0x411111f0}, - {0x19, 0x21a19030}, - {0x1e, 0x411111ff}), + {0x13, 0x90a60140}), SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, - {0x12, 0x90a60130}, - {0x13, 0x40000000}, - {0x14, 0x411111f0}, - {0x17, 0x90170140}, - {0x1d, 0x4068a36d}, - {0x21, 0x03211020}), + {0x17, 0x90170110}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_STANDARD_PINS, + {0x17, 0x90170140}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_STANDARD_PINS, + {0x17, 0x90170150}), {} }; @@ -6675,77 +6477,33 @@ static const struct hda_model_fixup alc662_fixup_models[] = { static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - {0x12, 0x4004c000}, {0x14, 0x01014010}, - {0x15, 0x411111f0}, - {0x16, 0x411111f0}, {0x18, 0x01a19020}, - {0x19, 0x411111f0}, {0x1a, 0x0181302f}, - {0x1b, 0x0221401f}, - {0x1c, 0x411111f0}, - {0x1d, 0x4054c601}, - {0x1e, 0x411111f0}), + {0x1b, 0x0221401f}), SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, {0x12, 0x99a30130}, {0x14, 0x90170110}, {0x15, 0x0321101f}, - {0x16, 0x03011020}, - {0x18, 0x40000008}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x41000001}, - {0x1e, 0x411111f0}, - {0x1f, 0x411111f0}), + {0x16, 0x03011020}), SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, {0x12, 0x99a30140}, {0x14, 0x90170110}, {0x15, 0x0321101f}, - {0x16, 0x03011020}, - {0x18, 0x40000008}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x41000001}, - {0x1e, 0x411111f0}, - {0x1f, 0x411111f0}), + {0x16, 0x03011020}), SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, {0x12, 0x99a30150}, {0x14, 0x90170110}, {0x15, 0x0321101f}, - {0x16, 0x03011020}, - {0x18, 0x40000008}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x41000001}, - {0x1e, 0x411111f0}, - {0x1f, 0x411111f0}), + {0x16, 0x03011020}), SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x411111f0}, {0x14, 0x90170110}, {0x15, 0x0321101f}, - {0x16, 0x03011020}, - {0x18, 0x40000008}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x41000001}, - {0x1e, 0x411111f0}, - {0x1f, 0x411111f0}), + {0x16, 0x03011020}), SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE, {0x12, 0x90a60130}, {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x40d6832d}, - {0x1e, 0x411111f0}, - {0x1f, 0x411111f0}), + {0x15, 0x0321101f}), {} }; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index c19e021..9bba275 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1526,7 +1526,7 @@ static struct snd_rawmidi_ops snd_hdsp_midi_input = static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int id) { - char buf[32]; + char buf[40]; hdsp->midi[id].id = id; hdsp->midi[id].rmidi = NULL; @@ -1537,7 +1537,7 @@ static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int i hdsp->midi[id].pending = 0; spin_lock_init (&hdsp->midi[id].lock); - sprintf (buf, "%s MIDI %d", card->shortname, id+1); + snprintf(buf, sizeof(buf), "%s MIDI %d", card->shortname, id + 1); if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0) return -1; @@ -2806,7 +2806,8 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); offset = ucontrol->id.index - 1; - snd_BUG_ON(offset < 0); + if (snd_BUG_ON(offset < 0)) + return -EINVAL; switch (hdsp->io_type) { case Digiface: diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index 6120a06..4373615 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/i2c.h> #include <linux/delay.h> +#include <linux/module.h> #include <sound/core.h> #include "pmac.h" @@ -101,6 +102,7 @@ static const struct i2c_device_id keywest_i2c_id[] = { { "keywest", 0 }, /* instantiated by us if needed */ { } }; +MODULE_DEVICE_TABLE(i2c, keywest_i2c_id); static struct i2c_driver keywest_driver = { .driver = { diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 1d651b8..225bfda 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -57,6 +57,7 @@ source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" +source "sound/soc/sti/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 669648b..134aca1 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ +obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 841d059..ba8def5 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -290,7 +290,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, int dir, dir_mask; int ret; - pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", + pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n", ssc_readl(ssc_p->ssc->regs, SR)); /* Enable PMC peripheral clock for this SSC */ diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index dd94fea..5741c0a 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -344,14 +344,8 @@ static int au1xpsc_pcm_drvprobe(struct platform_device *pdev) platform_set_drvdata(pdev, dmadata); - return snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform); -} - -static int au1xpsc_pcm_drvremove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - - return 0; + return devm_snd_soc_register_platform(&pdev->dev, + &au1xpsc_soc_platform); } static struct platform_driver au1xpsc_pcm_driver = { @@ -359,7 +353,6 @@ static struct platform_driver au1xpsc_pcm_driver = { .name = "au1xpsc-pcm", }, .probe = au1xpsc_pcm_drvprobe, - .remove = au1xpsc_pcm_drvremove, }; module_platform_driver(au1xpsc_pcm_driver); diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index 24cc7f4..fcf5a9a 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -312,14 +312,8 @@ static int alchemy_pcm_drvprobe(struct platform_device *pdev) platform_set_drvdata(pdev, ctx); - return snd_soc_register_platform(&pdev->dev, &alchemy_pcm_soc_platform); -} - -static int alchemy_pcm_drvremove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - - return 0; + return devm_snd_soc_register_platform(&pdev->dev, + &alchemy_pcm_soc_platform); } static struct platform_driver alchemy_pcmdma_driver = { @@ -327,7 +321,6 @@ static struct platform_driver alchemy_pcmdma_driver = { .name = "alchemy-pcm-dma", }, .probe = alchemy_pcm_drvprobe, - .remove = alchemy_pcm_drvremove, }; module_platform_driver(alchemy_pcmdma_driver); diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index e742ef6..38e853a 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -305,19 +305,9 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev) return -ENOMEM; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENODEV; - - ret = -EBUSY; - if (!devm_request_mem_region(&pdev->dev, iores->start, - resource_size(iores), - pdev->name)) - return -EBUSY; - - wd->mmio = devm_ioremap(&pdev->dev, iores->start, - resource_size(iores)); - if (!wd->mmio) - return -EBUSY; + wd->mmio = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(wd->mmio)) + return PTR_ERR(wd->mmio); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmares) diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 03fa1cb..8c435be 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -862,6 +862,8 @@ static const struct of_device_id bcm2835_i2s_of_match[] = { {}, }; +MODULE_DEVICE_TABLE(of, bcm2835_i2s_of_match); + static struct platform_driver bcm2835_i2s_driver = { .probe = bcm2835_i2s_probe, .driver = { diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 238913e..02ad260 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -450,13 +450,8 @@ static struct snd_soc_platform_driver bf5xx_ac97_soc_platform = { static int bf5xx_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &bf5xx_ac97_soc_platform); -} - -static int bf5xx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, + &bf5xx_ac97_soc_platform); } static struct platform_driver bf5xx_pcm_driver = { @@ -465,7 +460,6 @@ static struct platform_driver bf5xx_pcm_driver = { }, .probe = bf5xx_soc_platform_probe, - .remove = bf5xx_soc_platform_remove, }; module_platform_driver(bf5xx_pcm_driver); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index d95477a..6cba211d 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -342,13 +342,8 @@ static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = { static int bfin_i2s_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &bf5xx_i2s_soc_platform); -} - -static int bfin_i2s_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, + &bf5xx_i2s_soc_platform); } static struct platform_driver bfin_i2s_pcm_driver = { @@ -357,7 +352,6 @@ static struct platform_driver bfin_i2s_pcm_driver = { }, .probe = bfin_i2s_soc_platform_probe, - .remove = bfin_i2s_soc_platform_remove, }; module_platform_driver(bfin_i2s_pcm_driver); diff --git a/sound/soc/blackfin/bfin-eval-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c index 4229f76..fddfe00c 100644 --- a/sound/soc/blackfin/bfin-eval-adau1x61.c +++ b/sound/soc/blackfin/bfin-eval-adau1x61.c @@ -108,6 +108,7 @@ static struct snd_soc_dai_link bfin_eval_adau1x61_dai = { static struct snd_soc_card bfin_eval_adau1x61 = { .name = "bfin-eval-adau1x61", + .owner = THIS_MODULE, .driver_name = "eval-adau1x61", .dai_link = &bfin_eval_adau1x61_dai, .num_links = 1, diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 38b3dad..e8bed6b 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -156,33 +156,29 @@ static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1); static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0); /* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */ -static const unsigned int mic_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(mic_tlv, 0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0), 3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0), - 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0), -}; + 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0) +); /* {0, 0, 0, -6, 0, 6, 12, 18}dB */ -static const unsigned int aux_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(aux_tlv, 0, 2, TLV_DB_SCALE_ITEM(0, 0, 0), - 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0), -}; + 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0) +); /* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */ -static const unsigned int out_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(out_tlv, 0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1), 4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0), 5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0), - 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0), -}; + 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0) +); -static const unsigned int st_tlv[] = { - TLV_DB_RANGE_HEAD(8), +static const DECLARE_TLV_DB_RANGE(st_tlv, 0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0), 2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0), 4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0), @@ -190,8 +186,8 @@ static const unsigned int st_tlv[] = { 8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0), 10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0), 14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0), - 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0), -}; + 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0) +); /* Sidetone Gain = M * 2^(-5-N) */ struct st_gain { @@ -1028,10 +1024,8 @@ static int pm860x_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (dir == PM860X_CLK_DIR_OUT) pm860x->dir = PM860X_CLK_DIR_OUT; - else { - pm860x->dir = PM860X_CLK_DIR_IN; + else /* Slave mode is not supported */ return -EINVAL; - } return 0; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index efaafce..0c9733e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -53,6 +53,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271_I2C if I2C select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C + select SND_SOC_CS4349 if I2C select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C @@ -62,6 +63,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_BT_SCO select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C + select SND_SOC_GTM601 + select SND_SOC_ICS43432 select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C @@ -83,6 +86,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C + select SND_SOC_RT298 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C select SND_SOC_RT5645 if I2C @@ -102,6 +106,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STA350 if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS + select SND_SOC_STI_SAS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C select SND_SOC_TAS571X if I2C @@ -403,6 +408,11 @@ config SND_SOC_CS42XX8_I2C select SND_SOC_CS42XX8 select REGMAP_I2C +# Cirrus Logic CS4349 HiFi DAC +config SND_SOC_CS4349 + tristate "Cirrus Logic CS4349 CODEC" + depends on I2C + config SND_SOC_CX20442 tristate depends on TTY @@ -446,6 +456,12 @@ config SND_SOC_ES8328_SPI tristate select SND_SOC_ES8328 +config SND_SOC_GTM601 + tristate 'GTM601 UMTS modem audio codec' + +config SND_SOC_ICS43432 + tristate + config SND_SOC_ISABELLE tristate @@ -512,12 +528,18 @@ config SND_SOC_RL6231 config SND_SOC_RL6347A tristate default y if SND_SOC_RT286=y + default y if SND_SOC_RT298=y default m if SND_SOC_RT286=m + default m if SND_SOC_RT298=m config SND_SOC_RT286 tristate depends on I2C +config SND_SOC_RT298 + tristate + depends on I2C + config SND_SOC_RT5631 tristate "Realtek ALC5631/RT5631 CODEC" depends on I2C @@ -610,6 +632,9 @@ config SND_SOC_STA529 config SND_SOC_STAC9766 tristate +config SND_SOC_STI_SAS + tristate "codec Audio support for STI SAS codec" + config SND_SOC_TAS2552 tristate "Texas Instruments TAS2552 Mono Audio amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index cf160d9..4a32077 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -45,6 +45,7 @@ snd-soc-cs4271-i2c-objs := cs4271-i2c.o snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o +snd-soc-cs4349-objs := cs4349.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o @@ -55,6 +56,8 @@ snd-soc-dmic-objs := dmic.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o +snd-soc-gtm601-objs := gtm601.o +snd-soc-ics43432-objs := ics43432.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o @@ -79,6 +82,7 @@ snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt286-objs := rt286.o +snd-soc-rt298-objs := rt298.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o @@ -106,6 +110,7 @@ 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-sti-sas-objs := sti-sas.o snd-soc-tas5086-objs := tas5086.o snd-soc-tas571x-objs := tas571x.o snd-soc-tfa9879-objs := tfa9879.o @@ -232,6 +237,7 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o +obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o @@ -242,6 +248,8 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o +obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o +obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o @@ -266,6 +274,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o +obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o @@ -289,6 +298,7 @@ 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_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index c7d243d..affb192 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1335,11 +1335,10 @@ static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1); static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1); /* -1dB = Mute */ -static const unsigned int hs_gain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(hs_gain_tlv, 0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0), - 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0), -}; + 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0) +); static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0); diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 3cc69a6..9ef20db 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -202,19 +202,21 @@ static struct snd_soc_dai_driver ad1980_dai = { .formats = SND_SOC_STD_AC97_FMTS, }, }; +#define AD1980_VENDOR_ID 0x41445300 +#define AD1980_VENDOR_MASK 0xffffff00 + static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) { struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); unsigned int retry_cnt = 0; + int ret; do { - if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(ac97); - if (snd_soc_read(codec, AC97_RESET) == 0x0090) - return 1; - } + ret = snd_ac97_reset(ac97, true, AD1980_VENDOR_ID, + AD1980_VENDOR_MASK); + if (ret >= 0) + return 0; - soc_ac97_ops->reset(ac97); /* * Set bit 16slot in register 74h, then every slot will has only * 16 bits. This command is sent out in 20bit mode, in which @@ -223,8 +225,6 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) */ snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900); - if (snd_soc_read(codec, AC97_RESET) == 0x0090) - return 0; } while (retry_cnt++ < 10); dev_err(codec->dev, "Failed to reset: AC97 link error\n"); @@ -240,7 +240,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) u16 vendor_id2; u16 ext_status; - ac97 = snd_soc_new_ac97_codec(codec); + ac97 = snd_soc_new_ac97_codec(codec, 0, 0); if (IS_ERR(ac97)) { ret = PTR_ERR(ac97); dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); @@ -260,22 +260,10 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec) if (ret < 0) goto reset_err; - /* Read out vendor ID to make sure it is ad1980 */ - if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) { - ret = -ENODEV; - goto reset_err; - } - vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2); - - if (vendor_id2 != 0x5370) { - if (vendor_id2 != 0x5374) { - ret = -ENODEV; - goto reset_err; - } else { - dev_warn(codec->dev, - "Found AD1981 - only 2/2 IN/OUT Channels supported\n"); - } + if (vendor_id2 == 0x5374) { + dev_warn(codec->dev, + "Found AD1981 - only 2/2 IN/OUT Channels supported\n"); } /* unmute captures and playbacks volume */ diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index a431602..fe1353a 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -320,13 +320,12 @@ static const struct reg_default adau1373_reg_defaults[] = { { ADAU1373_DIGEN, 0x00 }, }; -static const unsigned int adau1373_out_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(adau1373_out_tlv, 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1), 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0), 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0), - 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0), -}; + 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0) +); static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0); static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1); @@ -381,12 +380,11 @@ static const char *adau1373_bass_hpf_cutoff_text[] = { "158Hz", "232Hz", "347Hz", "520Hz", }; -static const unsigned int adau1373_bass_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(adau1373_bass_tlv, 0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1), 3, 4, TLV_DB_SCALE_ITEM(950, 250, 0), - 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0), -}; + 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0) +); static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum, ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text); @@ -414,11 +412,10 @@ static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum, static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum, ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text); -static const unsigned int adau1373_3d_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(adau1373_3d_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), - 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120), -}; + 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120) +); static const char *adau1373_lr_mux_text[] = { "Mute", @@ -1534,7 +1531,6 @@ MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id); static struct i2c_driver adau1373_i2c_driver = { .driver = { .name = "adau1373", - .owner = THIS_MODULE, }, .probe = adau1373_i2c_probe, .remove = adau1373_i2c_remove, diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index ff7f846..de53c0d 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -915,7 +915,6 @@ MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); static struct i2c_driver adau1701_i2c_driver = { .driver = { .name = "adau1701", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(adau1701_dt_ids), }, .probe = adau1701_i2c_probe, diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 862796d..348ccb1 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -47,7 +47,6 @@ MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids); static struct i2c_driver adau1761_i2c_driver = { .driver = { .name = "adau1761", - .owner = THIS_MODULE, }, .probe = adau1761_i2c_probe, .remove = adau1761_i2c_remove, diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 2ce4362..0e32bba 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -45,7 +45,6 @@ MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids); static struct i2c_driver adau1781_i2c_driver = { .driver = { .name = "adau1781", - .owner = THIS_MODULE, }, .probe = adau1781_i2c_probe, .remove = adau1781_i2c_remove, diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 9700e8c..21e7394 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -46,7 +46,6 @@ MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", - .owner = THIS_MODULE, }, .probe = adau1977_i2c_probe, .remove = adau1977_i2c_remove, diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c index 66d9fce..52881fa 100644 --- a/sound/soc/codecs/adav803.c +++ b/sound/soc/codecs/adav803.c @@ -36,7 +36,6 @@ static int adav803_remove(struct i2c_client *client) static struct i2c_driver adav803_driver = { .driver = { .name = "adav803", - .owner = THIS_MODULE, }, .probe = adav803_probe, .remove = adav803_remove, diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 36d8425..198c924 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -113,7 +113,7 @@ #define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x)) -static struct reg_default adav80x_reg_defaults[] = { +static const struct reg_default adav80x_reg_defaults[] = { { ADAV80X_PLAYBACK_CTRL, 0x01 }, { ADAV80X_AUX_IN_CTRL, 0x01 }, { ADAV80X_REC_CTRL, 0x02 }, @@ -865,7 +865,6 @@ const struct regmap_config adav80x_regmap_config = { .val_bits = 8, .pad_bits = 1, .reg_bits = 7, - .read_flag_mask = 0x01, .max_register = ADAV80X_PLL_OUTE, diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 8670861..54428c6 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -444,7 +444,6 @@ MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id); static struct i2c_driver ak4535_i2c_driver = { .driver = { .name = "ak4535", - .owner = THIS_MODULE, }, .probe = ak4535_i2c_probe, .remove = ak4535_i2c_remove, diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 2d0ff45..b14176f 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -609,7 +609,6 @@ MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id); static struct i2c_driver ak4641_i2c_driver = { .driver = { .name = "ak4641", - .owner = THIS_MODULE, }, .probe = ak4641_i2c_probe, .remove = ak4641_i2c_remove, diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 7c0f6552..4a90143 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -64,12 +64,15 @@ #define FIL1_0 0x1c #define FIL1_1 0x1d #define FIL1_2 0x1e -#define FIL1_3 0x1f +#define FIL1_3 0x1f /* The maximum valid register for ak4642 */ #define PW_MGMT4 0x20 #define MD_CTL5 0x21 #define LO_MS 0x22 #define HP_MS 0x23 -#define SPK_MS 0x24 +#define SPK_MS 0x24 /* The maximum valid register for ak4643 */ +#define EQ_FBEQAB 0x25 +#define EQ_FBEQCD 0x26 +#define EQ_FBEQE 0x27 /* The maximum valid register for ak4648 */ /* PW_MGMT1*/ #define PMVCM (1 << 6) /* VCOM Power Management */ @@ -241,7 +244,7 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = { /* * ak4642 register cache */ -static const struct reg_default ak4642_reg[] = { +static const struct reg_default ak4643_reg[] = { { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 }, { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 }, @@ -254,6 +257,14 @@ static const struct reg_default ak4642_reg[] = { { 36, 0x00 }, }; +/* The default settings for 0x0 ~ 0x1f registers are the same for ak4642 + and ak4643. So we reuse the ak4643 reg_default for ak4642. + The valid registers for ak4642 are 0x0 ~ 0x1f which is a subset of ak4643, + so define NUM_AK4642_REG_DEFAULTS for ak4642. +*/ +#define ak4642_reg ak4643_reg +#define NUM_AK4642_REG_DEFAULTS (FIL1_3 + 1) + static const struct reg_default ak4648_reg[] = { { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 }, { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, @@ -535,15 +546,23 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { static const struct regmap_config ak4642_regmap = { .reg_bits = 8, .val_bits = 8, - .max_register = ARRAY_SIZE(ak4642_reg) + 1, + .max_register = FIL1_3, .reg_defaults = ak4642_reg, - .num_reg_defaults = ARRAY_SIZE(ak4642_reg), + .num_reg_defaults = NUM_AK4642_REG_DEFAULTS, +}; + +static const struct regmap_config ak4643_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SPK_MS, + .reg_defaults = ak4643_reg, + .num_reg_defaults = ARRAY_SIZE(ak4643_reg), }; static const struct regmap_config ak4648_regmap = { .reg_bits = 8, .val_bits = 8, - .max_register = ARRAY_SIZE(ak4648_reg) + 1, + .max_register = EQ_FBEQE, .reg_defaults = ak4648_reg, .num_reg_defaults = ARRAY_SIZE(ak4648_reg), }; @@ -553,7 +572,7 @@ static const struct ak4642_drvdata ak4642_drvdata = { }; static const struct ak4642_drvdata ak4643_drvdata = { - .regmap_config = &ak4642_regmap, + .regmap_config = &ak4643_regmap, }; static const struct ak4642_drvdata ak4648_drvdata = { @@ -626,7 +645,6 @@ MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id); static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", - .owner = THIS_MODULE, .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 0e59063..c73a9f6 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -663,7 +663,6 @@ MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id); static struct i2c_driver ak4671_i2c_driver = { .driver = { .name = "ak4671-codec", - .owner = THIS_MODULE, }, .probe = ak4671_i2c_probe, .remove = ak4671_i2c_remove, diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 0fc24e0..d2e3a3e 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -82,12 +82,11 @@ static int amp_mixer_event(struct snd_soc_dapm_widget *w, static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0); -static const unsigned int boost_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(boost_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), - 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), -}; + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = { @@ -1085,7 +1084,6 @@ MODULE_DEVICE_TABLE(of, alc5623_of_match); static struct i2c_driver alc5623_i2c_driver = { .driver = { .name = "alc562x-codec", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(alc5623_of_match), }, .probe = alc5623_i2c_probe, diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 607a63b..4d3ba33 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -35,7 +35,7 @@ /* * ALC5632 register cache */ -static struct reg_default alc5632_reg_defaults[] = { +static const struct reg_default alc5632_reg_defaults[] = { { 2, 0x8080 }, /* R2 - Speaker Output Volume */ { 4, 0x8080 }, /* R4 - Headphone Output Volume */ { 6, 0x8080 }, /* R6 - AUXOUT Volume */ @@ -146,11 +146,10 @@ static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0); /* -16.5db min scale, 1.5db steps, no mute */ static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0); -static const unsigned int boost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(boost_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), - 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0), -}; + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0) +); /* 0db min scale, 6 db steps, no mute */ static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); /* 0db min scalem 0.75db steps, no mute */ @@ -1183,7 +1182,6 @@ MODULE_DEVICE_TABLE(of, alc5632_of_match); static struct i2c_driver alc5632_i2c_driver = { .driver = { .name = "alc5632", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(alc5632_of_match), }, .probe = alc5632_i2c_probe, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 802e05e..8a2221a 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1366,7 +1366,7 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, { struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; - struct reg_default dac_comp[] = { + struct reg_sequence dac_comp[] = { { 0x80, 0x3 }, { ARIZONA_DAC_COMP_1, 0 }, { ARIZONA_DAC_COMP_2, 0 }, @@ -1504,7 +1504,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, else rates = &arizona_48k_bclk_rates[0]; - wl = snd_pcm_format_width(params_format(params)); + wl = params_width(params); if (tdm_slots) { arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n", @@ -1756,17 +1756,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) } EXPORT_SYMBOL_GPL(arizona_init_dai); -static irqreturn_t arizona_fll_clock_ok(int irq, void *data) -{ - struct arizona_fll *fll = data; - - arizona_fll_dbg(fll, "clock OK\n"); - - complete(&fll->ok); - - return IRQ_HANDLED; -} - static struct { unsigned int min; unsigned int max; @@ -2048,17 +2037,18 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll) static int arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; - unsigned long time_left; bool use_sync = false; int already_enabled = arizona_is_enabled_fll(fll); struct arizona_fll_cfg cfg; + int i; + unsigned int val; if (already_enabled < 0) return already_enabled; if (already_enabled) { /* Facilitate smooth refclk across the transition */ - regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x7, + regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9, ARIZONA_FLL1_GAIN_MASK, 0); regmap_update_bits_async(fll->arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, @@ -2110,9 +2100,6 @@ static int arizona_enable_fll(struct arizona_fll *fll) if (!already_enabled) pm_runtime_get(arizona->dev); - /* Clear any pending completions */ - try_wait_for_completion(&fll->ok); - regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); if (use_sync) @@ -2124,10 +2111,24 @@ static int arizona_enable_fll(struct arizona_fll *fll) regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, 0); - time_left = wait_for_completion_timeout(&fll->ok, - msecs_to_jiffies(250)); - if (time_left == 0) + arizona_fll_dbg(fll, "Waiting for FLL lock...\n"); + val = 0; + for (i = 0; i < 15; i++) { + if (i < 5) + usleep_range(200, 400); + else + msleep(20); + + regmap_read(arizona->regmap, + ARIZONA_INTERRUPT_RAW_STATUS_5, + &val); + if (val & (ARIZONA_FLL1_CLOCK_OK_STS << (fll->id - 1))) + break; + } + if (i == 15) arizona_fll_warn(fll, "Timed out waiting for lock\n"); + else + arizona_fll_dbg(fll, "FLL locked (%d polls)\n", i); return 0; } @@ -2212,11 +2213,8 @@ EXPORT_SYMBOL_GPL(arizona_set_fll); int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, int ok_irq, struct arizona_fll *fll) { - int ret; unsigned int val; - init_completion(&fll->ok); - fll->id = id; fll->base = base; fll->arizona = arizona; @@ -2238,13 +2236,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), "FLL%d clock OK", id); - ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, - arizona_fll_clock_ok, fll); - if (ret != 0) { - dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n", - id, ret); - } - regmap_update_bits(arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, 0); @@ -2313,6 +2304,82 @@ const struct snd_kcontrol_new arizona_adsp2_rate_controls[] = { }; EXPORT_SYMBOL_GPL(arizona_adsp2_rate_controls); +static bool arizona_eq_filter_unstable(bool mode, __be16 _a, __be16 _b) +{ + s16 a = be16_to_cpu(_a); + s16 b = be16_to_cpu(_b); + + if (!mode) { + return abs(a) >= 4096; + } else { + if (abs(b) >= 4096) + return true; + + return (abs((a << 16) / (4096 - b)) >= 4096 << 4); + } +} + +int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + struct soc_bytes *params = (void *)kcontrol->private_value; + unsigned int val; + __be16 *data; + int len; + int ret; + + len = params->num_regs * regmap_get_val_bytes(arizona->regmap); + + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + data[0] &= cpu_to_be16(ARIZONA_EQ1_B1_MODE); + + if (arizona_eq_filter_unstable(!!data[0], data[1], data[2]) || + arizona_eq_filter_unstable(true, data[4], data[5]) || + arizona_eq_filter_unstable(true, data[8], data[9]) || + arizona_eq_filter_unstable(true, data[12], data[13]) || + arizona_eq_filter_unstable(false, data[16], data[17])) { + dev_err(arizona->dev, "Rejecting unstable EQ coefficients\n"); + ret = -EINVAL; + goto out; + } + + ret = regmap_read(arizona->regmap, params->base, &val); + if (ret != 0) + goto out; + + val &= ~ARIZONA_EQ1_B1_MODE; + data[0] |= cpu_to_be16(val); + + ret = regmap_raw_write(arizona->regmap, params->base, data, len); + +out: + kfree(data); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_eq_coeff_put); + +int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + __be16 *data = (__be16 *)ucontrol->value.bytes.data; + s16 val = be16_to_cpu(*data); + + if (abs(val) >= 4096) { + dev_err(arizona->dev, "Rejecting unstable LHPF coefficients\n"); + return -EINVAL; + } + + return snd_soc_bytes_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 43deb04..ada0a41 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -194,6 +194,20 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \ ARIZONA_MIXER_ROUTES(name " Preloader", name "R") +#define ARIZONA_EQ_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = arizona_eq_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 20, .mask = ~ARIZONA_EQ1_B1_MODE }) } + +#define ARIZONA_LHPF_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = arizona_lhpf_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 1 }) } + #define ARIZONA_RATE_ENUM_SIZE 4 extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; @@ -229,6 +243,11 @@ extern int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir); @@ -242,7 +261,6 @@ struct arizona_fll { int id; unsigned int base; unsigned int vco_mult; - struct completion ok; unsigned int fout; int sync_src; diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 8f40025..44c30fe 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -74,33 +74,8 @@ static const struct reg_default cs35l32_reg_defaults[] = { static bool cs35l32_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS35L32_DEVID_AB: - case CS35L32_DEVID_CD: - case CS35L32_DEVID_E: - case CS35L32_FAB_ID: - case CS35L32_REV_ID: - case CS35L32_PWRCTL1: - case CS35L32_PWRCTL2: - case CS35L32_CLK_CTL: - case CS35L32_BATT_THRESHOLD: - case CS35L32_VMON: - case CS35L32_BST_CPCP_CTL: - case CS35L32_IMON_SCALING: - case CS35L32_AUDIO_LED_MNGR: - case CS35L32_ADSP_CTL: - case CS35L32_CLASSD_CTL: - case CS35L32_PROTECT_CTL: - case CS35L32_INT_MASK_1: - case CS35L32_INT_MASK_2: - case CS35L32_INT_MASK_3: - case CS35L32_INT_STATUS_1: - case CS35L32_INT_STATUS_2: - case CS35L32_INT_STATUS_3: - case CS35L32_LED_STATUS: - case CS35L32_FLASH_MODE: - case CS35L32_MOVIE_MODE: - case CS35L32_FLASH_TIMER: - case CS35L32_FLASH_INHIBIT: + case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR: + case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT: return true; default: return false; @@ -110,15 +85,8 @@ static bool cs35l32_readable_register(struct device *dev, unsigned int reg) static bool cs35l32_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS35L32_DEVID_AB: - case CS35L32_DEVID_CD: - case CS35L32_DEVID_E: - case CS35L32_FAB_ID: - case CS35L32_REV_ID: - case CS35L32_INT_STATUS_1: - case CS35L32_INT_STATUS_2: - case CS35L32_INT_STATUS_3: - case CS35L32_LED_STATUS: + case CS35L32_DEVID_AB ... CS35L32_REV_ID: + case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS: return true; default: return false; @@ -128,10 +96,7 @@ static bool cs35l32_volatile_register(struct device *dev, unsigned int reg) static bool cs35l32_precious_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS35L32_INT_STATUS_1: - case CS35L32_INT_STATUS_2: - case CS35L32_INT_STATUS_3: - case CS35L32_LED_STATUS: + case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS: return true; default: return false; @@ -276,7 +241,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { }; /* Current and threshold powerup sequence Pg37 in datasheet */ -static const struct reg_default cs35l32_monitor_patch[] = { +static const struct reg_sequence cs35l32_monitor_patch[] = { { 0x00, 0x99 }, { 0x48, 0x17 }, @@ -441,8 +406,7 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client, if (IS_ERR(cs35l32->reset_gpio)) return PTR_ERR(cs35l32->reset_gpio); - if (cs35l32->reset_gpio) - gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); /* initialize codec */ ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®); @@ -536,8 +500,7 @@ static int cs35l32_i2c_remove(struct i2c_client *i2c_client) snd_soc_unregister_codec(&i2c_client->dev); /* Hold down reset */ - if (cs35l32->reset_gpio) - gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); return 0; } @@ -551,8 +514,7 @@ static int cs35l32_runtime_suspend(struct device *dev) regcache_mark_dirty(cs35l32->regmap); /* Hold down reset */ - if (cs35l32->reset_gpio) - gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); /* remove power */ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), @@ -575,8 +537,7 @@ static int cs35l32_runtime_resume(struct device *dev) return ret; } - if (cs35l32->reset_gpio) - gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); regcache_cache_only(cs35l32->regmap, false); regcache_sync(cs35l32->regmap); @@ -607,7 +568,6 @@ MODULE_DEVICE_TABLE(i2c, cs35l32_id); static struct i2c_driver cs35l32_i2c_driver = { .driver = { .name = "cs35l32", - .owner = THIS_MODULE, .pm = &cs35l32_runtime_pm, .of_match_table = cs35l32_of_match, }, diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h index 31ab804..1d6c250 100644 --- a/sound/soc/codecs/cs35l32.h +++ b/sound/soc/codecs/cs35l32.h @@ -80,7 +80,7 @@ struct cs35l32_platform_data { #define CS35L32_GAIN_MGR_MASK 0x08 #define CS35L32_ADSP_SHARE_MASK 0x08 #define CS35L32_ADSP_DATACFG_MASK 0x30 -#define CS35L32_SDOUT_3ST 0x80 +#define CS35L32_SDOUT_3ST 0x08 #define CS35L32_BATT_REC_MASK 0x0E #define CS35L32_BATT_THRESH_MASK 0x30 diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 8e36198..55db19d 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -60,23 +60,7 @@ static const struct reg_default cs4265_reg_defaults[] = { static bool cs4265_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS4265_PWRCTL: - case CS4265_DAC_CTL: - case CS4265_ADC_CTL: - case CS4265_MCLK_FREQ: - case CS4265_SIG_SEL: - case CS4265_CHB_PGA_CTL: - case CS4265_CHA_PGA_CTL: - case CS4265_ADC_CTL2: - case CS4265_DAC_CHA_VOL: - case CS4265_DAC_CHB_VOL: - case CS4265_DAC_CTL2: - case CS4265_SPDIF_CTL1: - case CS4265_SPDIF_CTL2: - case CS4265_INT_MASK: - case CS4265_STATUS_MODE_MSB: - case CS4265_STATUS_MODE_LSB: - case CS4265_CHIP_ID: + case CS4265_CHIP_ID ... CS4265_SPDIF_CTL2: return true; default: return false; @@ -658,7 +642,6 @@ MODULE_DEVICE_TABLE(i2c, cs4265_id); static struct i2c_driver cs4265_i2c_driver = { .driver = { .name = "cs4265", - .owner = THIS_MODULE, .of_match_table = cs4265_of_match, }, .id_table = cs4265_id, diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index e6d4ff9..e07807d 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -751,7 +751,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id); static struct i2c_driver cs4270_i2c_driver = { .driver = { .name = "cs4270", - .owner = THIS_MODULE, .of_match_table = cs4270_of_match, }, .id_table = cs4270_id, diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c index b264da0..dcb3223 100644 --- a/sound/soc/codecs/cs4271-i2c.c +++ b/sound/soc/codecs/cs4271-i2c.c @@ -48,7 +48,6 @@ MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); static struct i2c_driver cs4271_i2c_driver = { .driver = { .name = "cs4271", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(cs4271_dt_ids), }, .probe = cs4271_i2c_probe, diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index c40428f..9bad478 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -45,7 +45,6 @@ static int cs42l51_i2c_remove(struct i2c_client *i2c) static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51", - .owner = THIS_MODULE, .of_match_table = cs42l51_of_match, }, .probe = cs42l51_i2c_probe, diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 4de52c9..47b97fc 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -110,58 +110,7 @@ static const struct reg_default cs42l52_reg_defaults[] = { static bool cs42l52_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS42L52_CHIP: - case CS42L52_PWRCTL1: - case CS42L52_PWRCTL2: - case CS42L52_PWRCTL3: - case CS42L52_CLK_CTL: - case CS42L52_IFACE_CTL1: - case CS42L52_IFACE_CTL2: - case CS42L52_ADC_PGA_A: - case CS42L52_ADC_PGA_B: - case CS42L52_ANALOG_HPF_CTL: - case CS42L52_ADC_HPF_FREQ: - case CS42L52_ADC_MISC_CTL: - case CS42L52_PB_CTL1: - case CS42L52_MISC_CTL: - case CS42L52_PB_CTL2: - case CS42L52_MICA_CTL: - case CS42L52_MICB_CTL: - case CS42L52_PGAA_CTL: - case CS42L52_PGAB_CTL: - case CS42L52_PASSTHRUA_VOL: - case CS42L52_PASSTHRUB_VOL: - case CS42L52_ADCA_VOL: - case CS42L52_ADCB_VOL: - case CS42L52_ADCA_MIXER_VOL: - case CS42L52_ADCB_MIXER_VOL: - case CS42L52_PCMA_MIXER_VOL: - case CS42L52_PCMB_MIXER_VOL: - case CS42L52_BEEP_FREQ: - case CS42L52_BEEP_VOL: - case CS42L52_BEEP_TONE_CTL: - case CS42L52_TONE_CTL: - case CS42L52_MASTERA_VOL: - case CS42L52_MASTERB_VOL: - case CS42L52_HPA_VOL: - case CS42L52_HPB_VOL: - case CS42L52_SPKA_VOL: - case CS42L52_SPKB_VOL: - case CS42L52_ADC_PCM_MIXER: - case CS42L52_LIMITER_CTL1: - case CS42L52_LIMITER_CTL2: - case CS42L52_LIMITER_AT_RATE: - case CS42L52_ALC_CTL: - case CS42L52_ALC_RATE: - case CS42L52_ALC_THRESHOLD: - case CS42L52_NOISE_GATE_CTL: - case CS42L52_CLK_STATUS: - case CS42L52_BATT_COMPEN: - case CS42L52_BATT_LEVEL: - case CS42L52_SPK_STATUS: - case CS42L52_TEM_CTL: - case CS42L52_THE_FOLDBACK: - case CS42L52_CHARGE_PUMP: + case CS42L52_CHIP ... CS42L52_CHARGE_PUMP: return true; default: return false; @@ -196,11 +145,10 @@ static DECLARE_TLV_DB_SCALE(mix_tlv, -50, 50, 0); static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0); -static const unsigned int limiter_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(limiter_tlv, 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), - 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), -}; + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0) +); static const char * const cs42l52_adca_text[] = { "Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"}; @@ -919,7 +867,7 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec, SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE) -static struct snd_soc_dai_ops cs42l52_ops = { +static const struct snd_soc_dai_ops cs42l52_ops = { .hw_params = cs42l52_pcm_hw_params, .digital_mute = cs42l52_digital_mute, .set_fmt = cs42l52_set_fmt, @@ -1118,7 +1066,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { }; /* Current and threshold powerup sequence Pg37 */ -static const struct reg_default cs42l52_threshold_patch[] = { +static const struct reg_sequence cs42l52_threshold_patch[] = { { 0x00, 0x99 }, { 0x3E, 0xBA }, @@ -1285,7 +1233,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l52_id); static struct i2c_driver cs42l52_i2c_driver = { .driver = { .name = "cs42l52", - .owner = THIS_MODULE, .of_match_table = cs42l52_of_match, }, .id_table = cs42l52_id, diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 1e11ba4..7cd5f76 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -115,52 +115,7 @@ static const struct reg_default cs42l56_reg_defaults[] = { static bool cs42l56_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS42L56_CHIP_ID_1: - case CS42L56_CHIP_ID_2: - case CS42L56_PWRCTL_1: - case CS42L56_PWRCTL_2: - case CS42L56_CLKCTL_1: - case CS42L56_CLKCTL_2: - case CS42L56_SERIAL_FMT: - case CS42L56_CLASSH_CTL: - case CS42L56_MISC_CTL: - case CS42L56_INT_STATUS: - case CS42L56_PLAYBACK_CTL: - case CS42L56_DSP_MUTE_CTL: - case CS42L56_ADCA_MIX_VOLUME: - case CS42L56_ADCB_MIX_VOLUME: - case CS42L56_PCMA_MIX_VOLUME: - case CS42L56_PCMB_MIX_VOLUME: - case CS42L56_ANAINPUT_ADV_VOLUME: - case CS42L56_DIGINPUT_ADV_VOLUME: - case CS42L56_MASTER_A_VOLUME: - case CS42L56_MASTER_B_VOLUME: - case CS42L56_BEEP_FREQ_ONTIME: - case CS42L56_BEEP_FREQ_OFFTIME: - case CS42L56_BEEP_TONE_CFG: - case CS42L56_TONE_CTL: - case CS42L56_CHAN_MIX_SWAP: - case CS42L56_AIN_REFCFG_ADC_MUX: - case CS42L56_HPF_CTL: - case CS42L56_MISC_ADC_CTL: - case CS42L56_GAIN_BIAS_CTL: - case CS42L56_PGAA_MUX_VOLUME: - case CS42L56_PGAB_MUX_VOLUME: - case CS42L56_ADCA_ATTENUATOR: - case CS42L56_ADCB_ATTENUATOR: - case CS42L56_ALC_EN_ATTACK_RATE: - case CS42L56_ALC_RELEASE_RATE: - case CS42L56_ALC_THRESHOLD: - case CS42L56_NOISE_GATE_CTL: - case CS42L56_ALC_LIM_SFT_ZC: - case CS42L56_AMUTE_HPLO_MUX: - case CS42L56_HPA_VOLUME: - case CS42L56_HPB_VOLUME: - case CS42L56_LOA_VOLUME: - case CS42L56_LOB_VOLUME: - case CS42L56_LIM_THRESHOLD_CTL: - case CS42L56_LIM_CTL_RELEASE_RATE: - case CS42L56_LIM_ATTACK_RATE: + case CS42L56_CHIP_ID_1 ... CS42L56_LIM_ATTACK_RATE: return true; default: return false; @@ -185,21 +140,18 @@ static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0); static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); -static const unsigned int ngnb_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(ngnb_tlv, 0, 1, TLV_DB_SCALE_ITEM(-8200, 600, 0), - 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0), -}; -static const unsigned int ngb_tlv[] = { - TLV_DB_RANGE_HEAD(2), + 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0) +); +static const DECLARE_TLV_DB_RANGE(ngb_tlv, 0, 2, TLV_DB_SCALE_ITEM(-6400, 600, 0), - 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0), -}; -static const unsigned int alc_tlv[] = { - TLV_DB_RANGE_HEAD(2), + 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0) +); +static const DECLARE_TLV_DB_RANGE(alc_tlv, 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), - 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), -}; + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0) +); static const char * const beep_config_text[] = { "Off", "Single", "Multiple", "Continuous" @@ -989,7 +941,7 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec, SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops cs42l56_ops = { +static const struct snd_soc_dai_ops cs42l56_ops = { .hw_params = cs42l56_pcm_hw_params, .digital_mute = cs42l56_digital_mute, .set_fmt = cs42l56_set_dai_fmt, @@ -1408,7 +1360,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l56_id); static struct i2c_driver cs42l56_i2c_driver = { .driver = { .name = "cs42l56", - .owner = THIS_MODULE, .of_match_table = cs42l56_of_match, }, .id_table = cs42l56_id, diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index b7853b9..42a8fd4 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -153,111 +153,18 @@ static bool cs42l73_volatile_register(struct device *dev, unsigned int reg) static bool cs42l73_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case CS42L73_DEVID_AB: - case CS42L73_DEVID_CD: - case CS42L73_DEVID_E: - case CS42L73_REVID: - case CS42L73_PWRCTL1: - case CS42L73_PWRCTL2: - case CS42L73_PWRCTL3: - case CS42L73_CPFCHC: - case CS42L73_OLMBMSDC: - case CS42L73_DMMCC: - case CS42L73_XSPC: - case CS42L73_XSPMMCC: - case CS42L73_ASPC: - case CS42L73_ASPMMCC: - case CS42L73_VSPC: - case CS42L73_VSPMMCC: - case CS42L73_VXSPFS: - case CS42L73_MIOPC: - case CS42L73_ADCIPC: - case CS42L73_MICAPREPGAAVOL: - case CS42L73_MICBPREPGABVOL: - case CS42L73_IPADVOL: - case CS42L73_IPBDVOL: - case CS42L73_PBDC: - case CS42L73_HLADVOL: - case CS42L73_HLBDVOL: - case CS42L73_SPKDVOL: - case CS42L73_ESLDVOL: - case CS42L73_HPAAVOL: - case CS42L73_HPBAVOL: - case CS42L73_LOAAVOL: - case CS42L73_LOBAVOL: - case CS42L73_STRINV: - case CS42L73_XSPINV: - case CS42L73_ASPINV: - case CS42L73_VSPINV: - case CS42L73_LIMARATEHL: - case CS42L73_LIMRRATEHL: - case CS42L73_LMAXHL: - case CS42L73_LIMARATESPK: - case CS42L73_LIMRRATESPK: - case CS42L73_LMAXSPK: - case CS42L73_LIMARATEESL: - case CS42L73_LIMRRATEESL: - case CS42L73_LMAXESL: - case CS42L73_ALCARATE: - case CS42L73_ALCRRATE: - case CS42L73_ALCMINMAX: - case CS42L73_NGCAB: - case CS42L73_ALCNGMC: - case CS42L73_MIXERCTL: - case CS42L73_HLAIPAA: - case CS42L73_HLBIPBA: - case CS42L73_HLAXSPAA: - case CS42L73_HLBXSPBA: - case CS42L73_HLAASPAA: - case CS42L73_HLBASPBA: - case CS42L73_HLAVSPMA: - case CS42L73_HLBVSPMA: - case CS42L73_XSPAIPAA: - case CS42L73_XSPBIPBA: - case CS42L73_XSPAXSPAA: - case CS42L73_XSPBXSPBA: - case CS42L73_XSPAASPAA: - case CS42L73_XSPAASPBA: - case CS42L73_XSPAVSPMA: - case CS42L73_XSPBVSPMA: - case CS42L73_ASPAIPAA: - case CS42L73_ASPBIPBA: - case CS42L73_ASPAXSPAA: - case CS42L73_ASPBXSPBA: - case CS42L73_ASPAASPAA: - case CS42L73_ASPBASPBA: - case CS42L73_ASPAVSPMA: - case CS42L73_ASPBVSPMA: - case CS42L73_VSPAIPAA: - case CS42L73_VSPBIPBA: - case CS42L73_VSPAXSPAA: - case CS42L73_VSPBXSPBA: - case CS42L73_VSPAASPAA: - case CS42L73_VSPBASPBA: - case CS42L73_VSPAVSPMA: - case CS42L73_VSPBVSPMA: - case CS42L73_MMIXCTL: - case CS42L73_SPKMIPMA: - case CS42L73_SPKMXSPA: - case CS42L73_SPKMASPA: - case CS42L73_SPKMVSPMA: - case CS42L73_ESLMIPMA: - case CS42L73_ESLMXSPA: - case CS42L73_ESLMASPA: - case CS42L73_ESLMVSPMA: - case CS42L73_IM1: - case CS42L73_IM2: + case CS42L73_DEVID_AB ... CS42L73_DEVID_E: + case CS42L73_REVID ... CS42L73_IM2: return true; default: return false; } } -static const unsigned int hpaloa_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(hpaloa_tlv, 0, 13, TLV_DB_SCALE_ITEM(-7600, 200, 0), - 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0), -}; + 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0) +); static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2500, 0); @@ -267,11 +174,10 @@ static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0); static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0); -static const unsigned int limiter_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(limiter_tlv, 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), - 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), -}; + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0) +); static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1); @@ -1236,8 +1142,8 @@ static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate) struct snd_soc_codec *codec = dai->codec; int id = dai->id; - return snd_soc_update_bits(codec, CS42L73_SPC(id), - 0x7F, tristate << 7); + return snd_soc_update_bits(codec, CS42L73_SPC(id), CS42L73_SP_3ST, + tristate << 7); } static const struct snd_pcm_hw_constraint_list constraints_12_24 = { @@ -1491,7 +1397,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l73_id); static struct i2c_driver cs42l73_i2c_driver = { .driver = { .name = "cs42l73", - .owner = THIS_MODULE, .of_match_table = cs42l73_of_match, }, .id_table = cs42l73_id, diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 657dce2..800c1d5 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -20,7 +20,7 @@ static int cs42xx8_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - u32 ret = cs42xx8_probe(&i2c->dev, + int ret = cs42xx8_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config)); if (ret) return ret; @@ -49,8 +49,8 @@ MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id); static struct i2c_driver cs42xx8_i2c_driver = { .driver = { .name = "cs42xx8", - .owner = THIS_MODULE, .pm = &cs42xx8_pm, + .of_match_table = cs42xx8_of_match, }, .probe = cs42xx8_i2c_probe, .remove = cs42xx8_i2c_remove, diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index e1d4686..d562e1b 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -425,7 +425,7 @@ const struct cs42xx8_driver_data cs42888_data = { }; EXPORT_SYMBOL_GPL(cs42888_data); -static const struct of_device_id cs42xx8_of_match[] = { +const struct of_device_id cs42xx8_of_match[] = { { .compatible = "cirrus,cs42448", .data = &cs42448_data, }, { .compatible = "cirrus,cs42888", .data = &cs42888_data, }, { /* sentinel */ } @@ -435,16 +435,24 @@ EXPORT_SYMBOL_GPL(cs42xx8_of_match); int cs42xx8_probe(struct device *dev, struct regmap *regmap) { - const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev); + const struct of_device_id *of_id; struct cs42xx8_priv *cs42xx8; int ret, val, i; + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to allocate regmap: %d\n", ret); + return ret; + } + cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL); if (cs42xx8 == NULL) return -ENOMEM; + cs42xx8->regmap = regmap; dev_set_drvdata(dev, cs42xx8); + of_id = of_match_device(cs42xx8_of_match, dev); if (of_id) cs42xx8->drvdata = of_id->data; @@ -482,13 +490,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap) /* Make sure hardware reset done */ msleep(5); - cs42xx8->regmap = regmap; - if (IS_ERR(cs42xx8->regmap)) { - ret = PTR_ERR(cs42xx8->regmap); - dev_err(dev, "failed to allocate regmap: %d\n", ret); - goto err_enable; - } - /* * We haven't marked the chip revision as volatile due to * sharing a register with the right input volume; explicitly diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h index b2c10e5..d36c61b 100644 --- a/sound/soc/codecs/cs42xx8.h +++ b/sound/soc/codecs/cs42xx8.h @@ -22,6 +22,7 @@ extern const struct dev_pm_ops cs42xx8_pm; extern const struct cs42xx8_driver_data cs42448_data; extern const struct cs42xx8_driver_data cs42888_data; extern const struct regmap_config cs42xx8_regmap_config; +extern const struct of_device_id cs42xx8_of_match[]; int cs42xx8_probe(struct device *dev, struct regmap *regmap); /* CS42888 register map */ diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c new file mode 100644 index 0000000..0ac8fc5 --- /dev/null +++ b/sound/soc/codecs/cs4349.c @@ -0,0 +1,392 @@ +/* + * cs4349.c -- CS4349 ALSA Soc Audio driver + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Authors: Tim Howe <Tim.Howe@cirrus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/regmap.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 "cs4349.h" + + +static const struct reg_default cs4349_reg_defaults[] = { + { 2, 0x00 }, /* r02 - Mode Control */ + { 3, 0x09 }, /* r03 - Volume, Mixing and Inversion Control */ + { 4, 0x81 }, /* r04 - Mute Control */ + { 5, 0x00 }, /* r05 - Channel A Volume Control */ + { 6, 0x00 }, /* r06 - Channel B Volume Control */ + { 7, 0xB1 }, /* r07 - Ramp and Filter Control */ + { 8, 0x1C }, /* r08 - Misc. Control */ +}; + +/* Private data for the CS4349 */ +struct cs4349_private { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int mode; + int rate; +}; + +static bool cs4349_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4349_CHIPID ... CS4349_MISC: + return true; + default: + return false; + } +} + +static bool cs4349_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4349_MODE ... CS4349_MISC: + return true; + default: + return false; + } +} + +static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec); + unsigned int fmt; + + fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; + + switch (fmt) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + cs4349->mode = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs4349_pcm_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 cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec); + int fmt, ret; + + cs4349->rate = params_rate(params); + + switch (cs4349->mode) { + case SND_SOC_DAIFMT_I2S: + fmt = DIF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + fmt = DIF_LEFT_JST; + break; + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 16: + fmt = DIF_RGHT_JST16; + break; + case 24: + fmt = DIF_RGHT_JST24; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, CS4349_MODE, DIF_MASK, + MODE_FORMAT(fmt)); + if (ret < 0) + return ret; + + return 0; +} + +static int cs4349_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int reg; + + reg = 0; + if (mute) + reg = MUTE_AB_MASK; + + return snd_soc_update_bits(codec, CS4349_MUTE, MUTE_AB_MASK, reg); +} + +static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0); + +static const char * const chan_mix_texts[] = { + "Mute", "MuteA", "MuteA SwapB", "MuteA MonoB", "SwapA MuteB", + "BothR", "Swap", "SwapA MonoB", "MuteB", "Normal", "BothL", + "MonoB", "MonoA MuteB", "MonoA", "MonoA SwapB", "Mono", + /*Normal == Channel A = Left, Channel B = Right*/ +}; + +static const char * const fm_texts[] = { + "Auto", "Single", "Double", "Quad", +}; + +static const char * const deemph_texts[] = { + "None", "44.1k", "48k", "32k", +}; + +static const char * const softr_zeroc_texts[] = { + "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC", +}; + +static int deemph_values[] = { + 0, 4, 8, 12, +}; + +static int softr_zeroc_values[] = { + 0, 64, 128, 192, +}; + +static const struct soc_enum chan_mix_enum = + SOC_ENUM_SINGLE(CS4349_VMI, 0, + ARRAY_SIZE(chan_mix_texts), + chan_mix_texts); + +static const struct soc_enum fm_mode_enum = + SOC_ENUM_SINGLE(CS4349_MODE, 0, + ARRAY_SIZE(fm_texts), + fm_texts); + +static SOC_VALUE_ENUM_SINGLE_DECL(deemph_enum, CS4349_MODE, 0, DEM_MASK, + deemph_texts, deemph_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(softr_zeroc_enum, CS4349_RMPFLT, 0, + SR_ZC_MASK, softr_zeroc_texts, + softr_zeroc_values); + +static const struct snd_kcontrol_new cs4349_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", + CS4349_VOLA, CS4349_VOLB, 0, 0xFF, 1, dig_tlv), + SOC_ENUM("Functional Mode", fm_mode_enum), + SOC_ENUM("De-Emphasis Control", deemph_enum), + SOC_ENUM("Soft Ramp Zero Cross Control", softr_zeroc_enum), + SOC_ENUM("Channel Mixer", chan_mix_enum), + SOC_SINGLE("VolA = VolB Switch", CS4349_VMI, 7, 1, 0), + SOC_SINGLE("InvertA Switch", CS4349_VMI, 6, 1, 0), + SOC_SINGLE("InvertB Switch", CS4349_VMI, 5, 1, 0), + SOC_SINGLE("Auto-Mute Switch", CS4349_MUTE, 7, 1, 0), + SOC_SINGLE("MUTEC A = B Switch", CS4349_MUTE, 5, 1, 0), + SOC_SINGLE("Soft Ramp Up Switch", CS4349_RMPFLT, 5, 1, 0), + SOC_SINGLE("Soft Ramp Down Switch", CS4349_RMPFLT, 4, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4349_RMPFLT, 2, 1, 0), + SOC_SINGLE("Freeze Switch", CS4349_MISC, 5, 1, 0), + SOC_SINGLE("Popguard Switch", CS4349_MISC, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget cs4349_dapm_widgets[] = { + SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OutputA"), + SND_SOC_DAPM_OUTPUT("OutputB"), +}; + +static const struct snd_soc_dapm_route cs4349_routes[] = { + {"DAC Playback", NULL, "OutputA"}, + {"DAC Playback", NULL, "OutputB"}, + + {"OutputA", NULL, "HiFi DAC"}, + {"OutputB", NULL, "HiFi DAC"}, +}; + +#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + 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) + +#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000 + +static const struct snd_soc_dai_ops cs4349_dai_ops = { + .hw_params = cs4349_pcm_hw_params, + .set_fmt = cs4349_set_dai_fmt, + .digital_mute = cs4349_digital_mute, +}; + +static struct snd_soc_dai_driver cs4349_dai = { + .name = "cs4349_hifi", + .playback = { + .stream_name = "DAC Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS4349_PCM_RATES, + .formats = CS4349_PCM_FORMATS, + }, + .ops = &cs4349_dai_ops, + .symmetric_rates = 1, +}; + +static struct snd_soc_codec_driver soc_codec_dev_cs4349 = { + .controls = cs4349_snd_controls, + .num_controls = ARRAY_SIZE(cs4349_snd_controls), + + .dapm_widgets = cs4349_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets), + .dapm_routes = cs4349_routes, + .num_dapm_routes = ARRAY_SIZE(cs4349_routes), +}; + +static const struct regmap_config cs4349_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS4349_MISC, + .reg_defaults = cs4349_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults), + .readable_reg = cs4349_readable_register, + .writeable_reg = cs4349_writeable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int cs4349_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs4349_private *cs4349; + int ret; + + cs4349 = devm_kzalloc(&client->dev, sizeof(*cs4349), GFP_KERNEL); + if (!cs4349) + return -ENOMEM; + + cs4349->regmap = devm_regmap_init_i2c(client, &cs4349_regmap); + if (IS_ERR(cs4349->regmap)) { + ret = PTR_ERR(cs4349->regmap); + dev_err(&client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs4349->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4349->reset_gpio)) + return PTR_ERR(cs4349->reset_gpio); + + gpiod_set_value_cansleep(cs4349->reset_gpio, 1); + + i2c_set_clientdata(client, cs4349); + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4349, + &cs4349_dai, 1); +} + +static int cs4349_i2c_remove(struct i2c_client *client) +{ + struct cs4349_private *cs4349 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs4349->reset_gpio, 0); + + return 0; +} + +#ifdef CONFIG_PM +static int cs4349_runtime_suspend(struct device *dev) +{ + struct cs4349_private *cs4349 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, PWR_DWN); + if (ret < 0) + return ret; + + regcache_cache_only(cs4349->regmap, true); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs4349->reset_gpio, 0); + + return 0; +} + +static int cs4349_runtime_resume(struct device *dev) +{ + struct cs4349_private *cs4349 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, 0); + if (ret < 0) + return ret; + + gpiod_set_value_cansleep(cs4349->reset_gpio, 1); + + regcache_cache_only(cs4349->regmap, false); + regcache_sync(cs4349->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops cs4349_runtime_pm = { + SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, + NULL) +}; + +static const struct of_device_id cs4349_of_match[] = { + { .compatible = "cirrus,cs4349", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, cs4349_of_match); + +static const struct i2c_device_id cs4349_i2c_id[] = { + {"cs4349", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id); + +static struct i2c_driver cs4349_i2c_driver = { + .driver = { + .name = "cs4349", + .of_match_table = cs4349_of_match, + }, + .id_table = cs4349_i2c_id, + .probe = cs4349_i2c_probe, + .remove = cs4349_i2c_remove, +}; + +module_i2c_driver(cs4349_i2c_driver); + +MODULE_AUTHOR("Tim Howe <tim.howe@cirrus.com>"); +MODULE_DESCRIPTION("Cirrus Logic CS4349 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4349.h b/sound/soc/codecs/cs4349.h new file mode 100644 index 0000000..d58c06a --- /dev/null +++ b/sound/soc/codecs/cs4349.h @@ -0,0 +1,136 @@ +/* + * ALSA SoC CS4349 codec driver + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Tim Howe <Tim.Howe@cirrus.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef __CS4349_H__ +#define __CS4349_H__ + +/* CS4349 registers addresses */ +#define CS4349_CHIPID 0x01 /* Device and Rev ID, Read Only */ +#define CS4349_MODE 0x02 /* Mode Control */ +#define CS4349_VMI 0x03 /* Volume, Mixing, Inversion Control */ +#define CS4349_MUTE 0x04 /* Mute Control */ +#define CS4349_VOLA 0x05 /* DAC Channel A Volume Control */ +#define CS4349_VOLB 0x06 /* DAC Channel B Volume Control */ +#define CS4349_RMPFLT 0x07 /* Ramp and Filter Control */ +#define CS4349_MISC 0x08 /* Power Down,Freeze Control,Pop Stop*/ + +#define CS4349_I2C_INCR 0x80 + + +/* Device and Revision ID */ +#define CS4349_REVA 0xF0 /* Rev A */ +#define CS4349_REVB 0xF1 /* Rev B */ +#define CS4349_REVC2 0xFF /* Rev C2 */ + + +/* PDN_DONE Poll Maximum + * If soft ramp is set it will take much longer to power down + * the system. + */ +#define PDN_POLL_MAX 900 + + +/* Bitfield Definitions */ + +/* CS4349_MODE */ +/* (Digital Interface Format, De-Emphasis Control, Functional Mode */ +#define DIF2 (1 << 6) +#define DIF1 (1 << 5) +#define DIF0 (1 << 4) +#define DEM1 (1 << 3) +#define DEM0 (1 << 2) +#define FM1 (1 << 1) +#define DIF_LEFT_JST 0x00 +#define DIF_I2S 0x01 +#define DIF_RGHT_JST16 0x02 +#define DIF_RGHT_JST24 0x03 +#define DIF_TDM0 0x04 +#define DIF_TDM1 0x05 +#define DIF_TDM2 0x06 +#define DIF_TDM3 0x07 +#define DIF_MASK 0x70 +#define MODE_FORMAT(x) (((x)&7)<<4) +#define DEM_MASK 0x0C +#define NO_DEM 0x00 +#define DEM_441 0x04 +#define DEM_48K 0x08 +#define DEM_32K 0x0C +#define FM_AUTO 0x00 +#define FM_SNGL 0x01 +#define FM_DBL 0x02 +#define FM_QUAD 0x03 +#define FM_SNGL_MIN 30000 +#define FM_SNGL_MAX 54000 +#define FM_DBL_MAX 108000 +#define FM_QUAD_MAX 216000 +#define FM_MASK 0x03 + +/* CS4349_VMI (VMI = Volume, Mixing and Inversion Controls) */ +#define VOLBISA (1 << 7) +#define VOLAISB (1 << 7) +/* INVERT_A only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */ +#define INVERT_A (1 << 6) +/* INVERT_B only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */ +#define INVERT_B (1 << 5) +#define ATAPI3 (1 << 3) +#define ATAPI2 (1 << 2) +#define ATAPI1 (1 << 1) +#define ATAPI0 (1 << 0) +#define MUTEAB 0x00 +#define MUTEA_RIGHTB 0x01 +#define MUTEA_LEFTB 0x02 +#define MUTEA_SUMLRDIV2B 0x03 +#define RIGHTA_MUTEB 0x04 +#define RIGHTA_RIGHTB 0x05 +#define RIGHTA_LEFTB 0x06 +#define RIGHTA_SUMLRDIV2B 0x07 +#define LEFTA_MUTEB 0x08 +#define LEFTA_RIGHTB 0x09 /* Default */ +#define LEFTA_LEFTB 0x0A +#define LEFTA_SUMLRDIV2B 0x0B +#define SUMLRDIV2A_MUTEB 0x0C +#define SUMLRDIV2A_RIGHTB 0x0D +#define SUMLRDIV2A_LEFTB 0x0E +#define SUMLRDIV2_AB 0x0F +#define CHMIX_MASK 0x0F + +/* CS4349_MUTE */ +#define AUTOMUTE (1 << 7) +#define MUTEC_AB (1 << 5) +#define MUTE_A (1 << 4) +#define MUTE_B (1 << 3) +#define MUTE_AB_MASK 0x18 + +/* CS4349_RMPFLT (Ramp and Filter Control) */ +#define SCZ1 (1 << 7) +#define SCZ0 (1 << 6) +#define RMP_UP (1 << 5) +#define RMP_DN (1 << 4) +#define FILT_SEL (1 << 2) +#define IMMDT_CHNG 0x31 +#define ZEROCRSS 0x71 +#define SOFT_RMP 0xB1 +#define SFTRMP_ZEROCRSS 0xF1 +#define SR_ZC_MASK 0xC0 + +/* CS4349_MISC */ +#define PWR_DWN (1 << 7) +#define FREEZE (1 << 5) +#define POPG_EN (1 << 4) + +#endif /* __CS4349_H__ */ diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 21810e5..7dc52fe 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -267,33 +267,29 @@ enum clk_src { * * Reserved area are considered as "mute". */ -static const unsigned int hp_out_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(hp_out_tlv, 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* -54 dB to +15 dB */ - 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0), -}; + 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) +); -static const unsigned int lineout_vol_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(lineout_vol_tlv, 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* -54dB to 15dB */ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) -}; +); -static const unsigned int mono_vol_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(mono_vol_tlv, 0x0, 0x2, TLV_DB_SCALE_ITEM(-1800, 0, 1), /* -18dB to 6dB */ 0x3, 0x7, TLV_DB_SCALE_ITEM(-1800, 600, 0) -}; +); -static const unsigned int aux1_vol_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(aux1_vol_tlv, 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* -48dB to 21dB */ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-4800, 150, 0) -}; +); static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); static const DECLARE_TLV_DB_SCALE(adc_eq_master_gain_tlv, -1800, 600, 1); @@ -680,7 +676,7 @@ struct da7210_priv { int master; }; -static struct reg_default da7210_reg_defaults[] = { +static const struct reg_default da7210_reg_defaults[] = { { 0x00, 0x00 }, { 0x01, 0x11 }, { 0x03, 0x00 }, @@ -1182,7 +1178,7 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = { #if IS_ENABLED(CONFIG_I2C) -static struct reg_default da7210_regmap_i2c_patch[] = { +static const struct reg_sequence da7210_regmap_i2c_patch[] = { /* System controller master disable */ { DA7210_STARTUP1, 0x00 }, @@ -1259,7 +1255,6 @@ MODULE_DEVICE_TABLE(i2c, da7210_i2c_id); static struct i2c_driver da7210_i2c_driver = { .driver = { .name = "da7210", - .owner = THIS_MODULE, }, .probe = da7210_i2c_probe, .remove = da7210_i2c_remove, @@ -1269,7 +1264,7 @@ static struct i2c_driver da7210_i2c_driver = { #if defined(CONFIG_SPI_MASTER) -static struct reg_default da7210_regmap_spi_patch[] = { +static const struct reg_sequence da7210_regmap_spi_patch[] = { /* Dummy read to give two pulses over nCS for SPI */ { DA7210_AUX2, 0x00 }, { DA7210_AUX2, 0x00 }, diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 238e48a..a9c86ef 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -28,27 +28,24 @@ /* Gain and Volume */ -static const unsigned int aux_vol_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(aux_vol_tlv, /* -54dB */ 0x0, 0x11, TLV_DB_SCALE_ITEM(-5400, 0, 0), /* -52.5dB to 15dB */ 0x12, 0x3f, TLV_DB_SCALE_ITEM(-5250, 150, 0) -}; +); -static const unsigned int digital_gain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(digital_gain_tlv, 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* -78dB to 12dB */ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) -}; +); -static const unsigned int alc_analog_gain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv, 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* 0dB to 36dB */ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) -}; +); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); @@ -954,7 +951,7 @@ static const struct snd_soc_dapm_route da7213_audio_map[] = { {"LINE", NULL, "Lineout PGA"}, }; -static struct reg_default da7213_reg_defaults[] = { +static const struct reg_default da7213_reg_defaults[] = { { DA7213_DIG_ROUTING_DAI, 0x10 }, { DA7213_SR, 0x0A }, { DA7213_REFERENCES, 0x80 }, @@ -1585,7 +1582,6 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); static struct i2c_driver da7213_i2c_driver = { .driver = { .name = "da7213", - .owner = THIS_MODULE, }, .probe = da7213_i2c_probe, .remove = da7213_remove, diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 2075236..1d5a89c 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -43,7 +43,7 @@ struct da732x_priv { /* * da732x register cache - default settings */ -static struct reg_default da732x_reg_cache[] = { +static const struct reg_default da732x_reg_cache[] = { { DA732X_REG_REF1 , 0x02 }, { DA732X_REG_BIAS_EN , 0x80 }, { DA732X_REG_BIAS1 , 0x00 }, @@ -1196,13 +1196,7 @@ static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, #define DA732X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops da732x_dai1_ops = { - .hw_params = da732x_hw_params, - .set_fmt = da732x_set_dai_fmt, - .set_sysclk = da732x_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops da732x_dai2_ops = { +static const struct snd_soc_dai_ops da732x_dai_ops = { .hw_params = da732x_hw_params, .set_fmt = da732x_set_dai_fmt, .set_sysclk = da732x_set_dai_sysclk, @@ -1227,7 +1221,7 @@ static struct snd_soc_dai_driver da732x_dai[] = { .rates = DA732X_RATES, .formats = DA732X_FORMATS, }, - .ops = &da732x_dai1_ops, + .ops = &da732x_dai_ops, }, { .name = "DA732X_AIFB", @@ -1247,7 +1241,7 @@ static struct snd_soc_dai_driver da732x_dai[] = { .rates = DA732X_RATES, .formats = DA732X_FORMATS, }, - .ops = &da732x_dai2_ops, + .ops = &da732x_dai_ops, }, }; @@ -1572,7 +1566,6 @@ MODULE_DEVICE_TABLE(i2c, da732x_i2c_id); static struct i2c_driver da732x_i2c_driver = { .driver = { .name = "da7320", - .owner = THIS_MODULE, }, .probe = da732x_i2c_probe, .remove = da732x_i2c_remove, diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 66bb446..0b2ede8 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -289,26 +289,23 @@ enum clk_src { /* Gain and Volume */ -static const unsigned int aux_vol_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(aux_vol_tlv, 0x0, 0x10, TLV_DB_SCALE_ITEM(-5400, 0, 0), /* -54dB to 15dB */ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) -}; +); -static const unsigned int digital_gain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(digital_gain_tlv, 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* -78dB to 12dB */ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) -}; +); -static const unsigned int alc_analog_gain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv, 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), /* 0dB to 36dB */ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) -}; +); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); @@ -948,7 +945,7 @@ struct da9055_priv { struct da9055_platform_data *pdata; }; -static struct reg_default da9055_reg_defaults[] = { +static const struct reg_default da9055_reg_defaults[] = { { 0x21, 0x10 }, { 0x22, 0x0A }, { 0x23, 0x00 }, @@ -1533,12 +1530,12 @@ static const struct of_device_id da9055_of_match[] = { { .compatible = "dlg,da9055-codec", }, { } }; +MODULE_DEVICE_TABLE(of, da9055_of_match); /* I2C codec control layer */ static struct i2c_driver da9055_i2c_driver = { .driver = { .name = "da9055-codec", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(da9055_of_match), }, .probe = da9055_i2c_probe, diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c new file mode 100644 index 0000000..0b80052 --- /dev/null +++ b/sound/soc/codecs/gtm601.c @@ -0,0 +1,95 @@ +/* + * This is a simple driver for the GTM601 Voice PCM interface + * + * Copyright (C) 2015 Goldelico GmbH + * + * Author: Marek Belisko <marek@goldelico.com> + * + * Based on wm8727.c driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/soc.h> + +static const struct snd_soc_dapm_widget gtm601_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("AOUT"), + SND_SOC_DAPM_INPUT("AIN"), +}; + +static const struct snd_soc_dapm_route gtm601_dapm_routes[] = { + { "AOUT", NULL, "Playback" }, + { "Capture", NULL, "AIN" }, +}; + +static struct snd_soc_dai_driver gtm601_dai = { + .name = "gtm601", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static const struct snd_soc_codec_driver soc_codec_dev_gtm601 = { + .dapm_widgets = gtm601_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets), + .dapm_routes = gtm601_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes), +}; + +static int gtm601_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_gtm601, >m601_dai, 1); +} + +static int gtm601_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id gtm601_codec_of_match[] = { + { .compatible = "option,gtm601", }, + {}, +}; +MODULE_DEVICE_TABLE(of, gtm601_codec_of_match); +#endif + +static struct platform_driver gtm601_codec_driver = { + .driver = { + .name = "gtm601", + .of_match_table = of_match_ptr(gtm601_codec_of_match), + }, + .probe = gtm601_platform_probe, + .remove = gtm601_platform_remove, +}; + +module_platform_driver(gtm601_codec_driver); + +MODULE_DESCRIPTION("ASoC gtm601 driver"); +MODULE_AUTHOR("Marek Belisko <marek@goldelico.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gtm601"); diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c new file mode 100644 index 0000000..dd850b9 --- /dev/null +++ b/sound/soc/codecs/ics43432.c @@ -0,0 +1,76 @@ +/* + * I2S MEMS microphone driver for InvenSense ICS-43432 + * + * - Non configurable. + * - I2S interface, 64 BCLs per frame, 32 bits per channel, 24 bit data + * + * Copyright (c) 2015 Axis Communications AB + * + * Licensed under GPL v2. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#define ICS43432_RATE_MIN 7190 /* Hz, from data sheet */ +#define ICS43432_RATE_MAX 52800 /* Hz, from data sheet */ + +#define ICS43432_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32) + +static struct snd_soc_dai_driver ics43432_dai = { + .name = "ics43432-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rate_min = ICS43432_RATE_MIN, + .rate_max = ICS43432_RATE_MAX, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = ICS43432_FORMATS, + }, +}; + +static struct snd_soc_codec_driver ics43432_codec_driver = { +}; + +static int ics43432_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &ics43432_codec_driver, + &ics43432_dai, 1); +} + +static int ics43432_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ics43432_ids[] = { + { .compatible = "invensense,ics43432", }, + { } +}; +MODULE_DEVICE_TABLE(of, ics43432_ids); +#endif + +static struct platform_driver ics43432_driver = { + .driver = { + .name = "ics43432", + .of_match_table = of_match_ptr(ics43432_ids), + }, + .probe = ics43432_probe, + .remove = ics43432_remove, +}; + +module_platform_driver(ics43432_driver); + +MODULE_DESCRIPTION("ASoC ICS43432 driver"); +MODULE_AUTHOR("Ricard Wanderlof <ricardw@axis.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index ebd9028..be44837 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -33,7 +33,7 @@ /* Register default values for ISABELLE driver. */ -static struct reg_default isabelle_reg_defs[] = { +static const struct reg_default isabelle_reg_defs[] = { { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, @@ -1016,25 +1016,25 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) #define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops isabelle_hs_dai_ops = { +static const struct snd_soc_dai_ops isabelle_hs_dai_ops = { .hw_params = isabelle_hw_params, .set_fmt = isabelle_set_dai_fmt, .digital_mute = isabelle_hs_mute, }; -static struct snd_soc_dai_ops isabelle_hf_dai_ops = { +static const struct snd_soc_dai_ops isabelle_hf_dai_ops = { .hw_params = isabelle_hw_params, .set_fmt = isabelle_set_dai_fmt, .digital_mute = isabelle_hf_mute, }; -static struct snd_soc_dai_ops isabelle_line_dai_ops = { +static const struct snd_soc_dai_ops isabelle_line_dai_ops = { .hw_params = isabelle_hw_params, .set_fmt = isabelle_set_dai_fmt, .digital_mute = isabelle_line_mute, }; -static struct snd_soc_dai_ops isabelle_ul_dai_ops = { +static const struct snd_soc_dai_ops isabelle_ul_dai_ops = { .hw_params = isabelle_hw_params, .set_fmt = isabelle_set_dai_fmt, }; @@ -1149,7 +1149,6 @@ MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id); static struct i2c_driver isabelle_i2c_driver = { .driver = { .name = "isabelle", - .owner = THIS_MODULE, }, .probe = isabelle_i2c_probe, .remove = isabelle_i2c_remove, diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 9363fdb..1f5ab99 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -78,11 +78,10 @@ struct jz4740_codec { struct regmap *regmap; }; -static const unsigned int jz4740_mic_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(jz4740_mic_tlv, 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0), - 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0) +); static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0); static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0); diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 99ffc49..558de10 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -142,7 +142,6 @@ MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); static struct i2c_driver lm4857_i2c_driver = { .driver = { .name = "lm4857", - .owner = THIS_MODULE, }, .probe = lm4857_i2c_probe, .id_table = lm4857_i2c_id, diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index 6600aa0..9af5640 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -30,7 +30,7 @@ #include <asm/div64.h> #include "lm49453.h" -static struct reg_default lm49453_reg_defs[] = { +static const struct reg_default lm49453_reg_defs[] = { { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, @@ -188,7 +188,6 @@ static struct reg_default lm49453_reg_defs[] = { /* codec private data */ struct lm49453_priv { struct regmap *regmap; - int fs_rate; }; /* capture path controls */ @@ -1112,13 +1111,10 @@ static int lm49453_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec); u16 clk_div = 0; - lm49453->fs_rate = params_rate(params); - /* Setting DAC clock dividers based on substream sample rate. */ - switch (lm49453->fs_rate) { + switch (params_rate(params)) { case 8000: case 16000: case 32000: @@ -1291,35 +1287,35 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec, #define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops lm49453_headset_dai_ops = { +static const struct snd_soc_dai_ops lm49453_headset_dai_ops = { .hw_params = lm49453_hw_params, .set_sysclk = lm49453_set_dai_sysclk, .set_fmt = lm49453_set_dai_fmt, .digital_mute = lm49453_hp_mute, }; -static struct snd_soc_dai_ops lm49453_speaker_dai_ops = { +static const struct snd_soc_dai_ops lm49453_speaker_dai_ops = { .hw_params = lm49453_hw_params, .set_sysclk = lm49453_set_dai_sysclk, .set_fmt = lm49453_set_dai_fmt, .digital_mute = lm49453_ls_mute, }; -static struct snd_soc_dai_ops lm49453_haptic_dai_ops = { +static const struct snd_soc_dai_ops lm49453_haptic_dai_ops = { .hw_params = lm49453_hw_params, .set_sysclk = lm49453_set_dai_sysclk, .set_fmt = lm49453_set_dai_fmt, .digital_mute = lm49453_ha_mute, }; -static struct snd_soc_dai_ops lm49453_ep_dai_ops = { +static const struct snd_soc_dai_ops lm49453_ep_dai_ops = { .hw_params = lm49453_hw_params, .set_sysclk = lm49453_set_dai_sysclk, .set_fmt = lm49453_set_dai_fmt, .digital_mute = lm49453_ep_mute, }; -static struct snd_soc_dai_ops lm49453_lineout_dai_ops = { +static const struct snd_soc_dai_ops lm49453_lineout_dai_ops = { .hw_params = lm49453_hw_params, .set_sysclk = lm49453_set_dai_sysclk, .set_fmt = lm49453_set_dai_fmt, @@ -1460,7 +1456,6 @@ MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id); static struct i2c_driver lm49453_i2c_driver = { .driver = { .name = "lm49453", - .owner = THIS_MODULE, }, .probe = lm49453_i2c_probe, .remove = lm49453_i2c_remove, diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index e1c196a..5b82e26 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -35,7 +35,7 @@ struct max9768 { u32 flags; }; -static struct reg_default max9768_default_regs[] = { +static const struct reg_default max9768_default_regs[] = { { 0, 0 }, { 3, MAX9768_CTRL_FILTERLESS}, }; @@ -43,8 +43,8 @@ static struct reg_default max9768_default_regs[] = { static int max9768_get_gpio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9768 *max9768 = snd_soc_component_get_drvdata(c); int val = gpio_get_value_cansleep(max9768->mute_gpio); ucontrol->value.integer.value[0] = !val; @@ -55,16 +55,15 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol, static int max9768_set_gpio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9768 *max9768 = snd_soc_component_get_drvdata(c); gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]); return 0; } -static const unsigned int volume_tlv[] = { - TLV_DB_RANGE_HEAD(43), +static const DECLARE_TLV_DB_RANGE(volume_tlv, 0, 0, TLV_DB_SCALE_ITEM(-16150, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(-9280, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(-9030, 0, 0), @@ -107,8 +106,8 @@ static const unsigned int volume_tlv[] = { 51, 57, TLV_DB_SCALE_ITEM(290, 50, 0), 58, 58, TLV_DB_SCALE_ITEM(650, 0, 0), 59, 62, TLV_DB_SCALE_ITEM(700, 60, 0), - 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0), -}; + 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0) +); static const struct snd_kcontrol_new max9768_volume[] = { SOC_SINGLE_TLV("Playback Volume", MAX9768_VOL, 0, 63, 0, volume_tlv), @@ -130,19 +129,20 @@ static const struct snd_soc_dapm_route max9768_dapm_routes[] = { { "OUT-", NULL, "IN" }, }; -static int max9768_probe(struct snd_soc_codec *codec) +static int max9768_probe(struct snd_soc_component *component) { - struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); + struct max9768 *max9768 = snd_soc_component_get_drvdata(component); int ret; if (max9768->flags & MAX9768_FLAG_CLASSIC_PWM) { - ret = snd_soc_write(codec, MAX9768_CTRL, MAX9768_CTRL_PWM); + ret = regmap_write(max9768->regmap, MAX9768_CTRL, + MAX9768_CTRL_PWM); if (ret) return ret; } if (gpio_is_valid(max9768->mute_gpio)) { - ret = snd_soc_add_codec_controls(codec, max9768_mute, + ret = snd_soc_add_component_controls(component, max9768_mute, ARRAY_SIZE(max9768_mute)); if (ret) return ret; @@ -151,7 +151,7 @@ static int max9768_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver max9768_codec_driver = { +static struct snd_soc_component_driver max9768_component_driver = { .probe = max9768_probe, .controls = max9768_volume, .num_controls = ARRAY_SIZE(max9768_volume), @@ -183,11 +183,13 @@ static int max9768_i2c_probe(struct i2c_client *client, if (pdata) { /* Mute on powerup to avoid clicks */ - err = gpio_request_one(pdata->mute_gpio, GPIOF_INIT_HIGH, "MAX9768 Mute"); + err = devm_gpio_request_one(&client->dev, pdata->mute_gpio, + GPIOF_INIT_HIGH, "MAX9768 Mute"); max9768->mute_gpio = err ?: pdata->mute_gpio; /* Activate chip by releasing shutdown, enables I2C */ - err = gpio_request_one(pdata->shdn_gpio, GPIOF_INIT_HIGH, "MAX9768 Shutdown"); + err = devm_gpio_request_one(&client->dev, pdata->shdn_gpio, + GPIOF_INIT_HIGH, "MAX9768 Shutdown"); max9768->shdn_gpio = err ?: pdata->shdn_gpio; max9768->flags = pdata->flags; @@ -199,38 +201,11 @@ static int max9768_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, max9768); max9768->regmap = devm_regmap_init_i2c(client, &max9768_i2c_regmap_config); - if (IS_ERR(max9768->regmap)) { - err = PTR_ERR(max9768->regmap); - goto err_gpio_free; - } + if (IS_ERR(max9768->regmap)) + return PTR_ERR(max9768->regmap); - err = snd_soc_register_codec(&client->dev, &max9768_codec_driver, NULL, 0); - if (err) - goto err_gpio_free; - - return 0; - - err_gpio_free: - if (gpio_is_valid(max9768->shdn_gpio)) - gpio_free(max9768->shdn_gpio); - if (gpio_is_valid(max9768->mute_gpio)) - gpio_free(max9768->mute_gpio); - - return err; -} - -static int max9768_i2c_remove(struct i2c_client *client) -{ - struct max9768 *max9768 = i2c_get_clientdata(client); - - snd_soc_unregister_codec(&client->dev); - - if (gpio_is_valid(max9768->shdn_gpio)) - gpio_free(max9768->shdn_gpio); - if (gpio_is_valid(max9768->mute_gpio)) - gpio_free(max9768->mute_gpio); - - return 0; + return devm_snd_soc_register_component(&client->dev, + &max9768_component_driver, NULL, 0); } static const struct i2c_device_id max9768_i2c_id[] = { @@ -242,10 +217,8 @@ MODULE_DEVICE_TABLE(i2c, max9768_i2c_id); static struct i2c_driver max9768_i2c_driver = { .driver = { .name = "max9768", - .owner = THIS_MODULE, }, .probe = max9768_i2c_probe, - .remove = max9768_i2c_remove, .id_table = max9768_i2c_id, }; module_i2c_driver(max9768_i2c_driver); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index d0f4534..20dcc49 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -258,292 +258,36 @@ static const struct reg_default max98088_reg[] = { { 0xc9, 0x00 }, /* C9 DAI2 biquad */ }; -static struct { - int readable; - int writable; - int vol; -} max98088_access[M98088_REG_CNT] = { - { 0xFF, 0xFF, 1 }, /* 00 IRQ status */ - { 0xFF, 0x00, 1 }, /* 01 MIC status */ - { 0xFF, 0x00, 1 }, /* 02 jack status */ - { 0x1F, 0x1F, 1 }, /* 03 battery voltage */ - { 0xFF, 0xFF, 0 }, /* 04 */ - { 0xFF, 0xFF, 0 }, /* 05 */ - { 0xFF, 0xFF, 0 }, /* 06 */ - { 0xFF, 0xFF, 0 }, /* 07 */ - { 0xFF, 0xFF, 0 }, /* 08 */ - { 0xFF, 0xFF, 0 }, /* 09 */ - { 0xFF, 0xFF, 0 }, /* 0A */ - { 0xFF, 0xFF, 0 }, /* 0B */ - { 0xFF, 0xFF, 0 }, /* 0C */ - { 0xFF, 0xFF, 0 }, /* 0D */ - { 0xFF, 0xFF, 0 }, /* 0E */ - { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */ - - { 0xFF, 0xFF, 0 }, /* 10 master clock */ - { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */ - { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */ - { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */ - { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */ - { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */ - { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */ - { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */ - { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */ - { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */ - { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */ - { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */ - { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */ - { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */ - { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */ - { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */ - - { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */ - { 0xFF, 0xFF, 0 }, /* 21 data config */ - { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */ - { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */ - { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */ - { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */ - { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */ - { 0xFF, 0xFF, 0 }, /* 27 HP control */ - { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */ - { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */ - { 0xFF, 0xFF, 0 }, /* 2A REC control */ - { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */ - { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */ - { 0xFF, 0xFF, 0 }, /* 2D SPK control */ - { 0xFF, 0xFF, 0 }, /* 2E sidetone */ - { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */ - - { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */ - { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */ - { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */ - { 0xFF, 0xFF, 0 }, /* 33 left ADC level */ - { 0xFF, 0xFF, 0 }, /* 34 right ADC level */ - { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */ - { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */ - { 0xFF, 0xFF, 0 }, /* 37 INA level */ - { 0xFF, 0xFF, 0 }, /* 38 INB level */ - { 0xFF, 0xFF, 0 }, /* 39 left HP volume */ - { 0xFF, 0xFF, 0 }, /* 3A right HP volume */ - { 0xFF, 0xFF, 0 }, /* 3B left REC volume */ - { 0xFF, 0xFF, 0 }, /* 3C right REC volume */ - { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */ - { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */ - { 0xFF, 0xFF, 0 }, /* 3F MIC config */ - - { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */ - { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */ - { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */ - { 0xFF, 0xFF, 0 }, /* 43 ALC */ - { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */ - { 0xFF, 0xFF, 0 }, /* 45 power limiter config */ - { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */ - { 0xFF, 0xFF, 0 }, /* 47 audio input */ - { 0xFF, 0xFF, 0 }, /* 48 microphone */ - { 0xFF, 0xFF, 0 }, /* 49 level control */ - { 0xFF, 0xFF, 0 }, /* 4A bypass switches */ - { 0xFF, 0xFF, 0 }, /* 4B jack detect */ - { 0xFF, 0xFF, 0 }, /* 4C input enable */ - { 0xFF, 0xFF, 0 }, /* 4D output enable */ - { 0xFF, 0xFF, 0 }, /* 4E bias control */ - { 0xFF, 0xFF, 0 }, /* 4F DAC power */ - - { 0xFF, 0xFF, 0 }, /* 50 DAC power */ - { 0xFF, 0xFF, 0 }, /* 51 system */ - { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */ - - { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */ - - { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */ - { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */ - - { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */ - { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */ - { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */ - - { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */ - { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */ - - { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */ - { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */ - { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */ - - { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */ - { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */ - { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */ - - { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */ - { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */ - { 0x00, 0x00, 0 }, /* CA */ - { 0x00, 0x00, 0 }, /* CB */ - { 0x00, 0x00, 0 }, /* CC */ - { 0x00, 0x00, 0 }, /* CD */ - { 0x00, 0x00, 0 }, /* CE */ - { 0x00, 0x00, 0 }, /* CF */ - - { 0x00, 0x00, 0 }, /* D0 */ - { 0x00, 0x00, 0 }, /* D1 */ - { 0x00, 0x00, 0 }, /* D2 */ - { 0x00, 0x00, 0 }, /* D3 */ - { 0x00, 0x00, 0 }, /* D4 */ - { 0x00, 0x00, 0 }, /* D5 */ - { 0x00, 0x00, 0 }, /* D6 */ - { 0x00, 0x00, 0 }, /* D7 */ - { 0x00, 0x00, 0 }, /* D8 */ - { 0x00, 0x00, 0 }, /* D9 */ - { 0x00, 0x00, 0 }, /* DA */ - { 0x00, 0x00, 0 }, /* DB */ - { 0x00, 0x00, 0 }, /* DC */ - { 0x00, 0x00, 0 }, /* DD */ - { 0x00, 0x00, 0 }, /* DE */ - { 0x00, 0x00, 0 }, /* DF */ - - { 0x00, 0x00, 0 }, /* E0 */ - { 0x00, 0x00, 0 }, /* E1 */ - { 0x00, 0x00, 0 }, /* E2 */ - { 0x00, 0x00, 0 }, /* E3 */ - { 0x00, 0x00, 0 }, /* E4 */ - { 0x00, 0x00, 0 }, /* E5 */ - { 0x00, 0x00, 0 }, /* E6 */ - { 0x00, 0x00, 0 }, /* E7 */ - { 0x00, 0x00, 0 }, /* E8 */ - { 0x00, 0x00, 0 }, /* E9 */ - { 0x00, 0x00, 0 }, /* EA */ - { 0x00, 0x00, 0 }, /* EB */ - { 0x00, 0x00, 0 }, /* EC */ - { 0x00, 0x00, 0 }, /* ED */ - { 0x00, 0x00, 0 }, /* EE */ - { 0x00, 0x00, 0 }, /* EF */ - - { 0x00, 0x00, 0 }, /* F0 */ - { 0x00, 0x00, 0 }, /* F1 */ - { 0x00, 0x00, 0 }, /* F2 */ - { 0x00, 0x00, 0 }, /* F3 */ - { 0x00, 0x00, 0 }, /* F4 */ - { 0x00, 0x00, 0 }, /* F5 */ - { 0x00, 0x00, 0 }, /* F6 */ - { 0x00, 0x00, 0 }, /* F7 */ - { 0x00, 0x00, 0 }, /* F8 */ - { 0x00, 0x00, 0 }, /* F9 */ - { 0x00, 0x00, 0 }, /* FA */ - { 0x00, 0x00, 0 }, /* FB */ - { 0x00, 0x00, 0 }, /* FC */ - { 0x00, 0x00, 0 }, /* FD */ - { 0x00, 0x00, 0 }, /* FE */ - { 0xFF, 0x00, 1 }, /* FF */ -}; - static bool max98088_readable_register(struct device *dev, unsigned int reg) { - return max98088_access[reg].readable; + switch (reg) { + case M98088_REG_00_IRQ_STATUS ... 0xC9: + case M98088_REG_FF_REV_ID: + return true; + default: + return false; + } +} + +static bool max98088_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98088_REG_03_BATTERY_VOLTAGE ... 0xC9: + return true; + default: + return false; + } } static bool max98088_volatile_register(struct device *dev, unsigned int reg) { - return max98088_access[reg].vol; + switch (reg) { + case M98088_REG_00_IRQ_STATUS ... M98088_REG_03_BATTERY_VOLTAGE: + case M98088_REG_FF_REV_ID: + return true; + default: + return false; + } } static const struct regmap_config max98088_regmap = { @@ -551,6 +295,7 @@ static const struct regmap_config max98088_regmap = { .val_bits = 8, .readable_reg = max98088_readable_register, + .writeable_reg = max98088_writeable_register, .volatile_reg = max98088_volatile_register, .max_register = 0xff, @@ -680,29 +425,26 @@ static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol, return 0; } -static const unsigned int max98088_micboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), - 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), - 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), -}; +static const DECLARE_TLV_DB_RANGE(max98088_micboost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); -static const unsigned int max98088_hp_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98088_hp_tlv, 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), - 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0), -}; + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0) +); -static const unsigned int max98088_spk_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98088_spk_tlv, 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), - 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0), -}; + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0) +); static const struct snd_kcontrol_new max98088_snd_controls[] = { @@ -2011,7 +1753,6 @@ MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); static struct i2c_driver max98088_i2c_driver = { .driver = { .name = "max98088", - .owner = THIS_MODULE, }, .probe = max98088_i2c_probe, .remove = max98088_i2c_remove, diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h index be89a4f..efa39bf 100644 --- a/sound/soc/codecs/max98088.h +++ b/sound/soc/codecs/max98088.h @@ -16,7 +16,7 @@ */ #define M98088_REG_00_IRQ_STATUS 0x00 #define M98088_REG_01_MIC_STATUS 0x01 -#define M98088_REG_02_JACK_STAUS 0x02 +#define M98088_REG_02_JACK_STATUS 0x02 #define M98088_REG_03_BATTERY_VOLTAGE 0x03 #define M98088_REG_0F_IRQ_ENABLE 0x0F #define M98088_REG_10_SYS_CLK 0x10 diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 78268f05..584aab8 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -267,75 +267,8 @@ static bool max98090_volatile_register(struct device *dev, unsigned int reg) static bool max98090_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case M98090_REG_DEVICE_STATUS: - case M98090_REG_JACK_STATUS: - case M98090_REG_INTERRUPT_S: - case M98090_REG_RESERVED: - case M98090_REG_LINE_INPUT_CONFIG: - case M98090_REG_LINE_INPUT_LEVEL: - case M98090_REG_INPUT_MODE: - case M98090_REG_MIC1_INPUT_LEVEL: - case M98090_REG_MIC2_INPUT_LEVEL: - case M98090_REG_MIC_BIAS_VOLTAGE: - case M98090_REG_DIGITAL_MIC_ENABLE: - case M98090_REG_DIGITAL_MIC_CONFIG: - case M98090_REG_LEFT_ADC_MIXER: - case M98090_REG_RIGHT_ADC_MIXER: - case M98090_REG_LEFT_ADC_LEVEL: - case M98090_REG_RIGHT_ADC_LEVEL: - case M98090_REG_ADC_BIQUAD_LEVEL: - case M98090_REG_ADC_SIDETONE: - case M98090_REG_SYSTEM_CLOCK: - case M98090_REG_CLOCK_MODE: - case M98090_REG_CLOCK_RATIO_NI_MSB: - case M98090_REG_CLOCK_RATIO_NI_LSB: - case M98090_REG_CLOCK_RATIO_MI_MSB: - case M98090_REG_CLOCK_RATIO_MI_LSB: - case M98090_REG_MASTER_MODE: - case M98090_REG_INTERFACE_FORMAT: - case M98090_REG_TDM_CONTROL: - case M98090_REG_TDM_FORMAT: - case M98090_REG_IO_CONFIGURATION: - case M98090_REG_FILTER_CONFIG: - case M98090_REG_DAI_PLAYBACK_LEVEL: - case M98090_REG_DAI_PLAYBACK_LEVEL_EQ: - case M98090_REG_LEFT_HP_MIXER: - case M98090_REG_RIGHT_HP_MIXER: - case M98090_REG_HP_CONTROL: - case M98090_REG_LEFT_HP_VOLUME: - case M98090_REG_RIGHT_HP_VOLUME: - case M98090_REG_LEFT_SPK_MIXER: - case M98090_REG_RIGHT_SPK_MIXER: - case M98090_REG_SPK_CONTROL: - case M98090_REG_LEFT_SPK_VOLUME: - case M98090_REG_RIGHT_SPK_VOLUME: - case M98090_REG_DRC_TIMING: - case M98090_REG_DRC_COMPRESSOR: - case M98090_REG_DRC_EXPANDER: - case M98090_REG_DRC_GAIN: - case M98090_REG_RCV_LOUTL_MIXER: - case M98090_REG_RCV_LOUTL_CONTROL: - case M98090_REG_RCV_LOUTL_VOLUME: - case M98090_REG_LOUTR_MIXER: - case M98090_REG_LOUTR_CONTROL: - case M98090_REG_LOUTR_VOLUME: - case M98090_REG_JACK_DETECT: - case M98090_REG_INPUT_ENABLE: - case M98090_REG_OUTPUT_ENABLE: - case M98090_REG_LEVEL_CONTROL: - case M98090_REG_DSP_FILTER_ENABLE: - case M98090_REG_BIAS_CONTROL: - case M98090_REG_DAC_CONTROL: - case M98090_REG_ADC_CONTROL: - case M98090_REG_DEVICE_SHUTDOWN: - case M98090_REG_EQUALIZER_BASE ... M98090_REG_EQUALIZER_BASE + 0x68: - case M98090_REG_RECORD_BIQUAD_BASE ... M98090_REG_RECORD_BIQUAD_BASE + 0x0E: - case M98090_REG_DMIC3_VOLUME: - case M98090_REG_DMIC4_VOLUME: - case M98090_REG_DMIC34_BQ_PREATTEN: - case M98090_REG_RECORD_TDM_SLOT: - case M98090_REG_SAMPLE_RATE: - case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E: + case M98090_REG_DEVICE_STATUS ... M98090_REG_INTERRUPT_S: + case M98090_REG_LINE_INPUT_CONFIG ... 0xD1: case M98090_REG_REVISION_ID: return true; default: @@ -360,22 +293,20 @@ static int max98090_reset(struct max98090_priv *max98090) return ret; } -static const unsigned int max98090_micboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(max98090_micboost_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), - 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), -}; + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv, -600, 600, 0); -static const unsigned int max98090_line_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(max98090_line_tlv, 0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0), - 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0), -}; + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0) +); static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0); @@ -391,38 +322,34 @@ static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_sdg_tlv, 50, 200, 0); -static const unsigned int max98090_mixout_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(max98090_mixout_tlv, 0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0), - 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0), -}; + 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0) +); -static const unsigned int max98090_hp_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98090_hp_tlv, 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), - 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0), -}; + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0) +); -static const unsigned int max98090_spk_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98090_spk_tlv, 0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0), 5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0), 11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0), 15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0), - 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0), -}; + 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0) +); -static const unsigned int max98090_rcv_lout_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98090_rcv_lout_tlv, 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), - 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0), -}; + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0) +); static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -850,6 +777,19 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w, return 0; } +static int max98090_shdn_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + if (event & SND_SOC_DAPM_POST_PMU) + max98090->shdn_pending = true; + + return 0; + +} + static const char *mic1_mux_text[] = { "IN12", "IN56" }; static SOC_ENUM_SINGLE_DECL(mic1_mux_enum, @@ -1158,9 +1098,11 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION, M98090_SDOEN_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE, - M98090_DIGMICL_SHIFT, 0, NULL, 0), + M98090_DIGMICL_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE, - M98090_DIGMICR_SHIFT, 0, NULL, 0), + M98090_DIGMICR_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG, M98090_AHPF_SHIFT, 0, NULL, 0), @@ -1205,10 +1147,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { &max98090_right_adc_mixer_controls[0], ARRAY_SIZE(max98090_right_adc_mixer_controls)), - SND_SOC_DAPM_ADC("ADCL", NULL, M98090_REG_INPUT_ENABLE, - M98090_ADLEN_SHIFT, 0), - SND_SOC_DAPM_ADC("ADCR", NULL, M98090_REG_INPUT_ENABLE, - M98090_ADREN_SHIFT, 0), + SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADLEN_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADREN_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), @@ -1801,10 +1745,13 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, if (IS_ERR(max98090->mclk)) break; - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { clk_disable_unprepare(max98090->mclk); - else - clk_prepare_enable(max98090->mclk); + } else { + ret = clk_prepare_enable(max98090->mclk); + if (ret) + return ret; + } break; case SND_SOC_BIAS_STANDBY: @@ -2383,7 +2330,7 @@ EXPORT_SYMBOL_GPL(max98090_mic_detect); #define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 #define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -static struct snd_soc_dai_ops max98090_dai_ops = { +static const struct snd_soc_dai_ops max98090_dai_ops = { .set_sysclk = max98090_dai_set_sysclk, .set_fmt = max98090_dai_set_fmt, .set_tdm_slot = max98090_set_tdm_slot, @@ -2536,9 +2483,26 @@ static int max98090_remove(struct snd_soc_codec *codec) return 0; } +static void max98090_seq_notifier(struct snd_soc_dapm_context *dapm, + enum snd_soc_dapm_type event, int subseq) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + if (max98090->shdn_pending) { + snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, 0); + msleep(40); + snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, M98090_SHDNN_MASK); + max98090->shdn_pending = false; + } +} + static struct snd_soc_codec_driver soc_codec_dev_max98090 = { .probe = max98090_probe, .remove = max98090_remove, + .seq_notifier = max98090_seq_notifier, .set_bias_level = max98090_set_bias_level, }; @@ -2714,7 +2678,6 @@ MODULE_DEVICE_TABLE(acpi, max98090_acpi_match); static struct i2c_driver max98090_i2c_driver = { .driver = { .name = "max98090", - .owner = THIS_MODULE, .pm = &max98090_pm, .of_match_table = of_match_ptr(max98090_of_match), .acpi_match_table = ACPI_PTR(max98090_acpi_match), diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index 21ff743..bc610d9 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1543,6 +1543,7 @@ struct max98090_priv { unsigned int pa2en; unsigned int sidetone; bool master; + bool shdn_pending; }; int max98090_mic_detect(struct snd_soc_codec *codec, diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 9a46d3d..1fedac5 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -202,300 +202,36 @@ static const struct reg_default max98095_reg_def[] = { { 0xff, 0x00 }, /* FF */ }; -static struct { - int readable; - int writable; -} max98095_access[M98095_REG_CNT] = { - { 0x00, 0x00 }, /* 00 */ - { 0xFF, 0x00 }, /* 01 */ - { 0xFF, 0x00 }, /* 02 */ - { 0xFF, 0x00 }, /* 03 */ - { 0xFF, 0x00 }, /* 04 */ - { 0xFF, 0x00 }, /* 05 */ - { 0xFF, 0x00 }, /* 06 */ - { 0xFF, 0x00 }, /* 07 */ - { 0xFF, 0x00 }, /* 08 */ - { 0xFF, 0x00 }, /* 09 */ - { 0xFF, 0x00 }, /* 0A */ - { 0xFF, 0x00 }, /* 0B */ - { 0xFF, 0x00 }, /* 0C */ - { 0xFF, 0x00 }, /* 0D */ - { 0xFF, 0x00 }, /* 0E */ - { 0xFF, 0x9F }, /* 0F */ - { 0xFF, 0xFF }, /* 10 */ - { 0xFF, 0xFF }, /* 11 */ - { 0xFF, 0xFF }, /* 12 */ - { 0xFF, 0xFF }, /* 13 */ - { 0xFF, 0xFF }, /* 14 */ - { 0xFF, 0xFF }, /* 15 */ - { 0xFF, 0xFF }, /* 16 */ - { 0xFF, 0xFF }, /* 17 */ - { 0xFF, 0xFF }, /* 18 */ - { 0xFF, 0xFF }, /* 19 */ - { 0xFF, 0xFF }, /* 1A */ - { 0xFF, 0xFF }, /* 1B */ - { 0xFF, 0xFF }, /* 1C */ - { 0xFF, 0xFF }, /* 1D */ - { 0xFF, 0x77 }, /* 1E */ - { 0xFF, 0x77 }, /* 1F */ - { 0xFF, 0x77 }, /* 20 */ - { 0xFF, 0x77 }, /* 21 */ - { 0xFF, 0x77 }, /* 22 */ - { 0xFF, 0x77 }, /* 23 */ - { 0xFF, 0xFF }, /* 24 */ - { 0xFF, 0x7F }, /* 25 */ - { 0xFF, 0x31 }, /* 26 */ - { 0xFF, 0xFF }, /* 27 */ - { 0xFF, 0xFF }, /* 28 */ - { 0xFF, 0xFF }, /* 29 */ - { 0xFF, 0xF7 }, /* 2A */ - { 0xFF, 0x2F }, /* 2B */ - { 0xFF, 0xEF }, /* 2C */ - { 0xFF, 0xFF }, /* 2D */ - { 0xFF, 0xFF }, /* 2E */ - { 0xFF, 0xFF }, /* 2F */ - { 0xFF, 0xFF }, /* 30 */ - { 0xFF, 0xFF }, /* 31 */ - { 0xFF, 0xFF }, /* 32 */ - { 0xFF, 0xFF }, /* 33 */ - { 0xFF, 0xF7 }, /* 34 */ - { 0xFF, 0x2F }, /* 35 */ - { 0xFF, 0xCF }, /* 36 */ - { 0xFF, 0xFF }, /* 37 */ - { 0xFF, 0xFF }, /* 38 */ - { 0xFF, 0xFF }, /* 39 */ - { 0xFF, 0xFF }, /* 3A */ - { 0xFF, 0xFF }, /* 3B */ - { 0xFF, 0xFF }, /* 3C */ - { 0xFF, 0xFF }, /* 3D */ - { 0xFF, 0xF7 }, /* 3E */ - { 0xFF, 0x2F }, /* 3F */ - { 0xFF, 0xCF }, /* 40 */ - { 0xFF, 0xFF }, /* 41 */ - { 0xFF, 0x77 }, /* 42 */ - { 0xFF, 0xFF }, /* 43 */ - { 0xFF, 0xFF }, /* 44 */ - { 0xFF, 0xFF }, /* 45 */ - { 0xFF, 0xFF }, /* 46 */ - { 0xFF, 0xFF }, /* 47 */ - { 0xFF, 0xFF }, /* 48 */ - { 0xFF, 0x0F }, /* 49 */ - { 0xFF, 0xFF }, /* 4A */ - { 0xFF, 0xFF }, /* 4B */ - { 0xFF, 0x3F }, /* 4C */ - { 0xFF, 0x3F }, /* 4D */ - { 0xFF, 0x3F }, /* 4E */ - { 0xFF, 0xFF }, /* 4F */ - { 0xFF, 0x7F }, /* 50 */ - { 0xFF, 0x7F }, /* 51 */ - { 0xFF, 0x0F }, /* 52 */ - { 0xFF, 0x3F }, /* 53 */ - { 0xFF, 0x3F }, /* 54 */ - { 0xFF, 0x3F }, /* 55 */ - { 0xFF, 0xFF }, /* 56 */ - { 0xFF, 0xFF }, /* 57 */ - { 0xFF, 0xBF }, /* 58 */ - { 0xFF, 0x1F }, /* 59 */ - { 0xFF, 0xBF }, /* 5A */ - { 0xFF, 0x1F }, /* 5B */ - { 0xFF, 0xBF }, /* 5C */ - { 0xFF, 0x3F }, /* 5D */ - { 0xFF, 0x3F }, /* 5E */ - { 0xFF, 0x7F }, /* 5F */ - { 0xFF, 0x7F }, /* 60 */ - { 0xFF, 0x47 }, /* 61 */ - { 0xFF, 0x9F }, /* 62 */ - { 0xFF, 0x9F }, /* 63 */ - { 0xFF, 0x9F }, /* 64 */ - { 0xFF, 0x9F }, /* 65 */ - { 0xFF, 0x9F }, /* 66 */ - { 0xFF, 0xBF }, /* 67 */ - { 0xFF, 0xBF }, /* 68 */ - { 0xFF, 0xFF }, /* 69 */ - { 0xFF, 0xFF }, /* 6A */ - { 0xFF, 0x7F }, /* 6B */ - { 0xFF, 0xF7 }, /* 6C */ - { 0xFF, 0xFF }, /* 6D */ - { 0xFF, 0xFF }, /* 6E */ - { 0xFF, 0x1F }, /* 6F */ - { 0xFF, 0xF7 }, /* 70 */ - { 0xFF, 0xFF }, /* 71 */ - { 0xFF, 0xFF }, /* 72 */ - { 0xFF, 0x1F }, /* 73 */ - { 0xFF, 0xF7 }, /* 74 */ - { 0xFF, 0xFF }, /* 75 */ - { 0xFF, 0xFF }, /* 76 */ - { 0xFF, 0x1F }, /* 77 */ - { 0xFF, 0xF7 }, /* 78 */ - { 0xFF, 0xFF }, /* 79 */ - { 0xFF, 0xFF }, /* 7A */ - { 0xFF, 0x1F }, /* 7B */ - { 0xFF, 0xF7 }, /* 7C */ - { 0xFF, 0xFF }, /* 7D */ - { 0xFF, 0xFF }, /* 7E */ - { 0xFF, 0x1F }, /* 7F */ - { 0xFF, 0xF7 }, /* 80 */ - { 0xFF, 0xFF }, /* 81 */ - { 0xFF, 0xFF }, /* 82 */ - { 0xFF, 0x1F }, /* 83 */ - { 0xFF, 0x7F }, /* 84 */ - { 0xFF, 0x0F }, /* 85 */ - { 0xFF, 0xD8 }, /* 86 */ - { 0xFF, 0xFF }, /* 87 */ - { 0xFF, 0xEF }, /* 88 */ - { 0xFF, 0xFE }, /* 89 */ - { 0xFF, 0xFE }, /* 8A */ - { 0xFF, 0xFF }, /* 8B */ - { 0xFF, 0xFF }, /* 8C */ - { 0xFF, 0x3F }, /* 8D */ - { 0xFF, 0xFF }, /* 8E */ - { 0xFF, 0x3F }, /* 8F */ - { 0xFF, 0x8F }, /* 90 */ - { 0xFF, 0xFF }, /* 91 */ - { 0xFF, 0x3F }, /* 92 */ - { 0xFF, 0xFF }, /* 93 */ - { 0xFF, 0xFF }, /* 94 */ - { 0xFF, 0x0F }, /* 95 */ - { 0xFF, 0x3F }, /* 96 */ - { 0xFF, 0x8C }, /* 97 */ - { 0x00, 0x00 }, /* 98 */ - { 0x00, 0x00 }, /* 99 */ - { 0x00, 0x00 }, /* 9A */ - { 0x00, 0x00 }, /* 9B */ - { 0x00, 0x00 }, /* 9C */ - { 0x00, 0x00 }, /* 9D */ - { 0x00, 0x00 }, /* 9E */ - { 0x00, 0x00 }, /* 9F */ - { 0x00, 0x00 }, /* A0 */ - { 0x00, 0x00 }, /* A1 */ - { 0x00, 0x00 }, /* A2 */ - { 0x00, 0x00 }, /* A3 */ - { 0x00, 0x00 }, /* A4 */ - { 0x00, 0x00 }, /* A5 */ - { 0x00, 0x00 }, /* A6 */ - { 0x00, 0x00 }, /* A7 */ - { 0x00, 0x00 }, /* A8 */ - { 0x00, 0x00 }, /* A9 */ - { 0x00, 0x00 }, /* AA */ - { 0x00, 0x00 }, /* AB */ - { 0x00, 0x00 }, /* AC */ - { 0x00, 0x00 }, /* AD */ - { 0x00, 0x00 }, /* AE */ - { 0x00, 0x00 }, /* AF */ - { 0x00, 0x00 }, /* B0 */ - { 0x00, 0x00 }, /* B1 */ - { 0x00, 0x00 }, /* B2 */ - { 0x00, 0x00 }, /* B3 */ - { 0x00, 0x00 }, /* B4 */ - { 0x00, 0x00 }, /* B5 */ - { 0x00, 0x00 }, /* B6 */ - { 0x00, 0x00 }, /* B7 */ - { 0x00, 0x00 }, /* B8 */ - { 0x00, 0x00 }, /* B9 */ - { 0x00, 0x00 }, /* BA */ - { 0x00, 0x00 }, /* BB */ - { 0x00, 0x00 }, /* BC */ - { 0x00, 0x00 }, /* BD */ - { 0x00, 0x00 }, /* BE */ - { 0x00, 0x00 }, /* BF */ - { 0x00, 0x00 }, /* C0 */ - { 0x00, 0x00 }, /* C1 */ - { 0x00, 0x00 }, /* C2 */ - { 0x00, 0x00 }, /* C3 */ - { 0x00, 0x00 }, /* C4 */ - { 0x00, 0x00 }, /* C5 */ - { 0x00, 0x00 }, /* C6 */ - { 0x00, 0x00 }, /* C7 */ - { 0x00, 0x00 }, /* C8 */ - { 0x00, 0x00 }, /* C9 */ - { 0x00, 0x00 }, /* CA */ - { 0x00, 0x00 }, /* CB */ - { 0x00, 0x00 }, /* CC */ - { 0x00, 0x00 }, /* CD */ - { 0x00, 0x00 }, /* CE */ - { 0x00, 0x00 }, /* CF */ - { 0x00, 0x00 }, /* D0 */ - { 0x00, 0x00 }, /* D1 */ - { 0x00, 0x00 }, /* D2 */ - { 0x00, 0x00 }, /* D3 */ - { 0x00, 0x00 }, /* D4 */ - { 0x00, 0x00 }, /* D5 */ - { 0x00, 0x00 }, /* D6 */ - { 0x00, 0x00 }, /* D7 */ - { 0x00, 0x00 }, /* D8 */ - { 0x00, 0x00 }, /* D9 */ - { 0x00, 0x00 }, /* DA */ - { 0x00, 0x00 }, /* DB */ - { 0x00, 0x00 }, /* DC */ - { 0x00, 0x00 }, /* DD */ - { 0x00, 0x00 }, /* DE */ - { 0x00, 0x00 }, /* DF */ - { 0x00, 0x00 }, /* E0 */ - { 0x00, 0x00 }, /* E1 */ - { 0x00, 0x00 }, /* E2 */ - { 0x00, 0x00 }, /* E3 */ - { 0x00, 0x00 }, /* E4 */ - { 0x00, 0x00 }, /* E5 */ - { 0x00, 0x00 }, /* E6 */ - { 0x00, 0x00 }, /* E7 */ - { 0x00, 0x00 }, /* E8 */ - { 0x00, 0x00 }, /* E9 */ - { 0x00, 0x00 }, /* EA */ - { 0x00, 0x00 }, /* EB */ - { 0x00, 0x00 }, /* EC */ - { 0x00, 0x00 }, /* ED */ - { 0x00, 0x00 }, /* EE */ - { 0x00, 0x00 }, /* EF */ - { 0x00, 0x00 }, /* F0 */ - { 0x00, 0x00 }, /* F1 */ - { 0x00, 0x00 }, /* F2 */ - { 0x00, 0x00 }, /* F3 */ - { 0x00, 0x00 }, /* F4 */ - { 0x00, 0x00 }, /* F5 */ - { 0x00, 0x00 }, /* F6 */ - { 0x00, 0x00 }, /* F7 */ - { 0x00, 0x00 }, /* F8 */ - { 0x00, 0x00 }, /* F9 */ - { 0x00, 0x00 }, /* FA */ - { 0x00, 0x00 }, /* FB */ - { 0x00, 0x00 }, /* FC */ - { 0x00, 0x00 }, /* FD */ - { 0x00, 0x00 }, /* FE */ - { 0xFF, 0x00 }, /* FF */ -}; - static bool max98095_readable(struct device *dev, unsigned int reg) { - if (reg >= M98095_REG_CNT) - return 0; - return max98095_access[reg].readable != 0; + switch (reg) { + case M98095_001_HOST_INT_STS ... M98095_097_PWR_SYS: + case M98095_0FF_REV_ID: + return true; + default: + return false; + } } -static bool max98095_volatile(struct device *dev, unsigned int reg) +static bool max98095_writeable(struct device *dev, unsigned int reg) { - if (reg > M98095_REG_MAX_CACHED) - return 1; - switch (reg) { - case M98095_000_HOST_DATA: - case M98095_001_HOST_INT_STS: - case M98095_002_HOST_RSP_STS: - case M98095_003_HOST_CMD_STS: - case M98095_004_CODEC_STS: - case M98095_005_DAI1_ALC_STS: - case M98095_006_DAI2_ALC_STS: - case M98095_007_JACK_AUTO_STS: - case M98095_008_JACK_MANUAL_STS: - case M98095_009_JACK_VBAT_STS: - case M98095_00A_ACC_ADC_STS: - case M98095_00B_MIC_NG_AGC_STS: - case M98095_00C_SPK_L_VOLT_STS: - case M98095_00D_SPK_R_VOLT_STS: - case M98095_00E_TEMP_SENSOR_STS: - return 1; + case M98095_00F_HOST_CFG ... M98095_097_PWR_SYS: + return true; + default: + return false; } +} - return 0; +static bool max98095_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98095_000_HOST_DATA ... M98095_00E_TEMP_SENSOR_STS: + case M98095_REG_MAX_CACHED + 1 ... M98095_0FF_REV_ID: + return true; + default: + return false; + } } static const struct regmap_config max98095_regmap = { @@ -508,6 +244,7 @@ static const struct regmap_config max98095_regmap = { .cache_type = REGCACHE_RBTREE, .readable_reg = max98095_readable, + .writeable_reg = max98095_writeable, .volatile_reg = max98095_volatile, }; @@ -661,48 +398,43 @@ static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol, return 0; } -static const unsigned int max98095_micboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(max98095_micboost_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), - 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), -}; + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0); static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0); -static const unsigned int max98095_hp_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98095_hp_tlv, 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), - 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0), -}; + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0) +); -static const unsigned int max98095_spk_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(max98095_spk_tlv, 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0), 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0), 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0), - 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0), -}; + 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0) +); -static const unsigned int max98095_rcv_lout_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(max98095_rcv_lout_tlv, 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), - 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0), -}; + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0) +); -static const unsigned int max98095_lin_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(max98095_lin_tlv, 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0), 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0), - 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0), -}; + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0) +); static const struct snd_kcontrol_new max98095_snd_controls[] = { @@ -1653,10 +1385,13 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, if (IS_ERR(max98095->mclk)) break; - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { clk_disable_unprepare(max98095->mclk); - else - clk_prepare_enable(max98095->mclk); + } else { + ret = clk_prepare_enable(max98095->mclk); + if (ret) + return ret; + } break; case SND_SOC_BIAS_STANDBY: @@ -2431,7 +2166,6 @@ MODULE_DEVICE_TABLE(of, max98095_of_match); static struct i2c_driver max98095_i2c_driver = { .driver = { .name = "max98095", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(max98095_of_match), }, .probe = max98095_i2c_probe, diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index 3a2fda0..f5e3dce 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -31,6 +31,9 @@ static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, { struct gpio_desc *sdmode = snd_soc_dai_get_drvdata(dai); + if (!sdmode) + return 0; + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -48,24 +51,21 @@ static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, } static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = { - SND_SOC_DAPM_DAC("SDMode", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("Speaker"), }; static const struct snd_soc_dapm_route max98357a_dapm_routes[] = { - {"Speaker", NULL, "SDMode"}, + {"Speaker", NULL, "HiFi Playback"}, }; static int max98357a_codec_probe(struct snd_soc_codec *codec) { struct gpio_desc *sdmode; - sdmode = devm_gpiod_get(codec->dev, "sdmode", GPIOD_OUT_LOW); - if (IS_ERR(sdmode)) { - dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n", - __func__, PTR_ERR(sdmode)); + sdmode = devm_gpiod_get_optional(codec->dev, "sdmode", GPIOD_OUT_LOW); + if (IS_ERR(sdmode)) return PTR_ERR(sdmode); - } + snd_soc_codec_set_drvdata(codec, sdmode); return 0; @@ -79,7 +79,7 @@ static struct snd_soc_codec_driver max98357a_codec_driver = { .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes), }; -static struct snd_soc_dai_ops max98357a_dai_ops = { +static const struct snd_soc_dai_ops max98357a_dai_ops = { .trigger = max98357a_daiops_trigger, }; @@ -104,15 +104,8 @@ static struct snd_soc_dai_driver max98357a_dai_driver = { static int max98357a_platform_probe(struct platform_device *pdev) { - int ret; - - ret = snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver, + return snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver, &max98357a_dai_driver, 1); - if (ret) - dev_err(&pdev->dev, "%s() error registering codec driver: %d\n", - __func__, ret); - - return ret; } static int max98357a_platform_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 481d58f1..c14a79d 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -67,13 +67,12 @@ static const struct regmap_config max9850_regmap = { .cache_type = REGCACHE_RBTREE, }; -static const unsigned int max9850_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(max9850_tlv, 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0), 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0), - 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0), -}; + 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0) +); static const struct snd_kcontrol_new max9850_controls[] = { SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv), @@ -352,7 +351,6 @@ MODULE_DEVICE_TABLE(i2c, max9850_i2c_id); static struct i2c_driver max9850_i2c_driver = { .driver = { .name = "max9850", - .owner = THIS_MODULE, }, .probe = max9850_i2c_probe, .remove = max9850_i2c_remove, diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 29549cd..61cc18e 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -20,9 +20,7 @@ #include "max9877.h" -static struct regmap *regmap; - -static struct reg_default max9877_regs[] = { +static const struct reg_default max9877_regs[] = { { 0, 0x40 }, { 1, 0x00 }, { 2, 0x00 }, @@ -30,19 +28,17 @@ static struct reg_default max9877_regs[] = { { 4, 0x49 }, }; -static const unsigned int max9877_pgain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(max9877_pgain_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0), - 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0), -}; + 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0) +); -static const unsigned int max9877_output_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(max9877_output_tlv, 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1), 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0), 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0), - 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0), -}; + 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0) +); static const char *max9877_out_mode[] = { "INA -> SPK", @@ -123,7 +119,7 @@ static const struct snd_soc_dapm_route max9877_dapm_routes[] = { { "HPR", NULL, "SHDN" }, }; -static const struct snd_soc_codec_driver max9877_codec = { +static const struct snd_soc_component_driver max9877_component_driver = { .controls = max9877_controls, .num_controls = ARRAY_SIZE(max9877_controls), @@ -145,6 +141,7 @@ static const struct regmap_config max9877_regmap = { static int max9877_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct regmap *regmap; int i; regmap = devm_regmap_init_i2c(client, &max9877_regmap); @@ -155,14 +152,8 @@ static int max9877_i2c_probe(struct i2c_client *client, for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def); - return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0); -} - -static int max9877_i2c_remove(struct i2c_client *client) -{ - snd_soc_unregister_codec(&client->dev); - - return 0; + return devm_snd_soc_register_component(&client->dev, + &max9877_component_driver, NULL, 0); } static const struct i2c_device_id max9877_i2c_id[] = { @@ -174,10 +165,8 @@ MODULE_DEVICE_TABLE(i2c, max9877_i2c_id); static struct i2c_driver max9877_i2c_driver = { .driver = { .name = "max9877", - .owner = THIS_MODULE, }, .probe = max9877_i2c_probe, - .remove = max9877_i2c_remove, .id_table = max9877_i2c_id, }; diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index aad6642..5990de3 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -271,8 +271,6 @@ static inline int max98925_rate_value(struct snd_soc_codec *codec, break; } } - dev_dbg(codec->dev, "%s: sample rate is %d, returning %d\n", - __func__, rate_table[i].rate, *value); return ret; } @@ -432,7 +430,7 @@ static int max98925_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); - switch (snd_pcm_format_width(params_format(params))) { + switch (params_width(params)) { case 16: regmap_update_bits(max98925->regmap, MAX98925_FORMAT, @@ -523,7 +521,6 @@ static int max98925_probe(struct snd_soc_codec *codec) struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); max98925->codec = codec; - codec->control_data = max98925->regmap; regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00); /* It's not the default but we need to set DAI_DLY */ regmap_write(max98925->regmap, @@ -639,7 +636,6 @@ MODULE_DEVICE_TABLE(of, max98925_of_match); static struct i2c_driver max98925_i2c_driver = { .driver = { .name = "max98925", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(max98925_of_match), .pm = NULL, }, diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 3d44fc5..3e770cb 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -650,14 +650,14 @@ static int mc13783_remove(struct snd_soc_codec *codec) #define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) -static struct snd_soc_dai_ops mc13783_ops_dac = { +static const struct snd_soc_dai_ops mc13783_ops_dac = { .hw_params = mc13783_pcm_hw_params_dac, .set_fmt = mc13783_set_fmt_async, .set_sysclk = mc13783_set_sysclk_dac, .set_tdm_slot = mc13783_set_tdm_slot_dac, }; -static struct snd_soc_dai_ops mc13783_ops_codec = { +static const struct snd_soc_dai_ops mc13783_ops_codec = { .hw_params = mc13783_pcm_hw_params_codec, .set_fmt = mc13783_set_fmt_async, .set_sysclk = mc13783_set_sysclk_codec, @@ -698,7 +698,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = { }, }; -static struct snd_soc_dai_ops mc13783_ops_sync = { +static const struct snd_soc_dai_ops mc13783_ops_sync = { .hw_params = mc13783_pcm_hw_params_sync, .set_fmt = mc13783_set_fmt_sync, .set_sysclk = mc13783_set_sysclk_sync, diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index b74118e..f561c78 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -199,7 +199,7 @@ static const struct clk_coeff coeff_div[] = { {12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4}, }; -static struct reg_default ml26124_reg[] = { +static const struct reg_default ml26124_reg[] = { /* CLOCK control Register */ {0x00, 0x00 }, /* Sampling Rate */ {0x02, 0x00}, /* PLL NL */ @@ -597,7 +597,6 @@ MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id); static struct i2c_driver ml26124_i2c_driver = { .driver = { .name = "ml26124", - .owner = THIS_MODULE, }, .probe = ml26124_i2c_probe, .remove = ml26124_i2c_remove, diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index e7ba557..5832523 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -95,17 +95,22 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec) struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); int i = 0, val = -1, enable = 0; - if (priv->deemph) - for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) - if (pcm1681_deemph[i] == priv->rate) + if (priv->deemph) { + for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) { + if (pcm1681_deemph[i] == priv->rate) { val = i; + break; + } + } + } if (val != -1) { regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, PCM1681_DEEMPH_RATE_MASK, val << 3); enable = 1; - } else + } else { enable = 0; + } /* enable/disable deemphasis functionality */ return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, @@ -330,7 +335,6 @@ static int pcm1681_i2c_remove(struct i2c_client *client) static struct i2c_driver pcm1681_i2c_driver = { .driver = { .name = "pcm1681", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(pcm1681_dt_ids), }, .id_table = pcm1681_i2c_id, diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index dcdfac0..dbff416 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -67,7 +67,6 @@ static struct i2c_driver pcm512x_i2c_driver = { .id_table = pcm512x_i2c_id, .driver = { .name = "pcm512x", - .owner = THIS_MODULE, .of_match_table = pcm512x_of_match, .pm = &pcm512x_pm_ops, }, diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index de16429..047c489 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1117,7 +1117,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, params_rate(params), params_channels(params)); - switch (snd_pcm_format_width(params_format(params))) { + switch (params_width(params)) { case 16: alen = PCM512x_ALEN_16; break; @@ -1132,7 +1132,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, break; default: dev_err(codec->dev, "Bad frame size: %d\n", - snd_pcm_format_width(params_format(params))); + params_width(params)); return -EINVAL; } diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index 56650d6..aca479f 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -11,38 +11,98 @@ */ #include <linux/module.h> +#include <linux/regmap.h> #include "rl6231.h" /** - * rl6231_calc_dmic_clk - Calculate the parameter of dmic. + * rl6231_get_pre_div - Return the value of pre divider. + * + * @map: map for setting. + * @reg: register. + * @sft: shift. + * + * Return the value of pre divider from given register value. + * Return negative error code for unexpected register value. + */ +int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft) +{ + int pd, val; + + regmap_read(map, reg, &val); + + val = (val >> sft) & 0x7; + + switch (val) { + case 0: + case 1: + case 2: + case 3: + pd = val + 1; + break; + case 4: + pd = 6; + break; + case 5: + pd = 8; + break; + case 6: + pd = 12; + break; + case 7: + pd = 16; + break; + default: + pd = -EINVAL; + break; + } + + return pd; +} +EXPORT_SYMBOL_GPL(rl6231_get_pre_div); + +/** + * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic. * * @rate: base clock rate. * - * Choose dmic clock between 1MHz and 3MHz. - * It is better for clock to approximate 3MHz. + * Choose divider parameter that gives the highest possible DMIC frequency in + * 1MHz - 3MHz range. */ int rl6231_calc_dmic_clk(int rate) { - int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL; - int i, red, bound, temp; + int div[] = {2, 3, 4, 6, 8, 12}; + int i; + + if (rate < 1000000 * div[0]) { + pr_warn("Base clock rate %d is too low\n", rate); + return -EINVAL; + } - red = 3000000 * 12; for (i = 0; i < ARRAY_SIZE(div); i++) { - bound = div[i] * 3000000; - if (rate > bound) - continue; - temp = bound - rate; - if (temp < red) { - red = temp; - idx = i; - } + /* find divider that gives DMIC frequency below 3MHz */ + if (3000000 * div[i] >= rate) + return i; } - return idx; + pr_warn("Base clock rate %d is too high\n", rate); + return -EINVAL; } EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk); +struct pll_calc_map { + unsigned int pll_in; + unsigned int pll_out; + int k; + int n; + int m; + bool m_bp; +}; + +static const struct pll_calc_map pll_preset_table[] = { + {19200000, 24576000, 3, 30, 3, false}, +}; + /** * rl6231_pll_calc - Calcualte PLL M/N/K code. * @freq_in: external clock provided to codec. @@ -57,7 +117,7 @@ int rl6231_pll_calc(const unsigned int freq_in, const unsigned int freq_out, struct rl6231_pll_code *pll_code) { int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX; - int k, red, n_t, pll_out, in_t, out_t; + int i, k, red, n_t, pll_out, in_t, out_t; int n = 0, m = 0, m_t = 0; int red_t = abs(freq_out - freq_in); bool bypass = false; @@ -65,6 +125,18 @@ int rl6231_pll_calc(const unsigned int freq_in, if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in) return -EINVAL; + for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { + if (freq_in == pll_preset_table[i].pll_in && + freq_out == pll_preset_table[i].pll_out) { + k = pll_preset_table[i].k; + m = pll_preset_table[i].m; + n = pll_preset_table[i].n; + bypass = pll_preset_table[i].m_bp; + pr_debug("Use preset PLL parameter table\n"); + goto code_find; + } + } + k = 100000000 / freq_out - 2; if (k > RL6231_PLL_K_MAX) k = RL6231_PLL_K_MAX; diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h index 0f7b057..4c77b44 100644 --- a/sound/soc/codecs/rl6231.h +++ b/sound/soc/codecs/rl6231.h @@ -30,5 +30,6 @@ int rl6231_calc_dmic_clk(int rate); int rl6231_pll_calc(const unsigned int freq_in, const unsigned int freq_out, struct rl6231_pll_code *pll_code); int rl6231_get_clk_info(int sclk, int rate); +int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft); #endif /* __RL6231_H__ */ diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 5c43e26..bd93658 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -38,7 +38,7 @@ #define RT288_VENDOR_ID 0x10ec0288 struct rt286_priv { - struct reg_default *index_cache; + const struct reg_default *index_cache; int index_cache_size; struct regmap *regmap; struct snd_soc_codec *codec; @@ -50,7 +50,7 @@ struct rt286_priv { int clk_id; }; -static struct reg_default rt286_index_def[] = { +static const struct reg_default rt286_index_def[] = { { 0x01, 0xaaaa }, { 0x02, 0x8aaa }, { 0x03, 0x0002 }, @@ -1108,7 +1108,7 @@ static const struct acpi_device_id rt286_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, rt286_acpi_match); -static struct dmi_system_id force_combo_jack_table[] = { +static const struct dmi_system_id force_combo_jack_table[] = { { .ident = "Intel Wilson Beach", .matches = { @@ -1118,7 +1118,7 @@ static struct dmi_system_id force_combo_jack_table[] = { { } }; -static struct dmi_system_id dmi_dell_dino[] = { +static const struct dmi_system_id dmi_dell_dino[] = { { .ident = "Dell Dino", .matches = { @@ -1157,7 +1157,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, } if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) { dev_err(&i2c->dev, - "Device with ID register %x is not rt286\n", val); + "Device with ID register %#x is not rt286\n", val); return -ENODEV; } @@ -1259,7 +1259,6 @@ static int rt286_i2c_remove(struct i2c_client *i2c) static struct i2c_driver rt286_i2c_driver = { .driver = { .name = "rt286", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(rt286_acpi_match), }, .probe = rt286_i2c_probe, diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c new file mode 100644 index 0000000..3c2f0f8 --- /dev/null +++ b/sound/soc/codecs/rt298.c @@ -0,0 +1,1271 @@ +/* + * rt298.c -- RT298 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.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/jack.h> +#include <linux/workqueue.h> +#include <sound/rt298.h> +#include <sound/hda_verbs.h> + +#include "rl6347a.h" +#include "rt298.h" + +#define RT298_VENDOR_ID 0x10ec0298 + +struct rt298_priv { + struct reg_default *index_cache; + int index_cache_size; + struct regmap *regmap; + struct snd_soc_codec *codec; + struct rt298_platform_data pdata; + struct i2c_client *i2c; + struct snd_soc_jack *jack; + struct delayed_work jack_detect_work; + int sys_clk; + int clk_id; + int is_hp_in; +}; + +static struct reg_default rt298_index_def[] = { + { 0x01, 0xaaaa }, + { 0x02, 0x8aaa }, + { 0x03, 0x0002 }, + { 0x04, 0xaf01 }, + { 0x08, 0x000d }, + { 0x09, 0xd810 }, + { 0x0a, 0x0120 }, + { 0x0b, 0x0000 }, + { 0x0d, 0x2800 }, + { 0x0f, 0x0000 }, + { 0x19, 0x0a17 }, + { 0x20, 0x0020 }, + { 0x33, 0x0208 }, + { 0x46, 0x0300 }, + { 0x49, 0x0004 }, + { 0x4f, 0x50e9 }, + { 0x50, 0x2000 }, + { 0x63, 0x2902 }, + { 0x67, 0x1111 }, + { 0x68, 0x1016 }, + { 0x69, 0x273f }, +}; +#define INDEX_CACHE_SIZE ARRAY_SIZE(rt298_index_def) + +static const struct reg_default rt298_reg[] = { + { 0x00170500, 0x00000400 }, + { 0x00220000, 0x00000031 }, + { 0x00239000, 0x0000007f }, + { 0x0023a000, 0x0000007f }, + { 0x00270500, 0x00000400 }, + { 0x00370500, 0x00000400 }, + { 0x00870500, 0x00000400 }, + { 0x00920000, 0x00000031 }, + { 0x00935000, 0x000000c3 }, + { 0x00936000, 0x000000c3 }, + { 0x00970500, 0x00000400 }, + { 0x00b37000, 0x00000097 }, + { 0x00b37200, 0x00000097 }, + { 0x00b37300, 0x00000097 }, + { 0x00c37000, 0x00000000 }, + { 0x00c37100, 0x00000080 }, + { 0x01270500, 0x00000400 }, + { 0x01370500, 0x00000400 }, + { 0x01371f00, 0x411111f0 }, + { 0x01439000, 0x00000080 }, + { 0x0143a000, 0x00000080 }, + { 0x01470700, 0x00000000 }, + { 0x01470500, 0x00000400 }, + { 0x01470c00, 0x00000000 }, + { 0x01470100, 0x00000000 }, + { 0x01837000, 0x00000000 }, + { 0x01870500, 0x00000400 }, + { 0x02050000, 0x00000000 }, + { 0x02139000, 0x00000080 }, + { 0x0213a000, 0x00000080 }, + { 0x02170100, 0x00000000 }, + { 0x02170500, 0x00000400 }, + { 0x02170700, 0x00000000 }, + { 0x02270100, 0x00000000 }, + { 0x02370100, 0x00000000 }, + { 0x01870700, 0x00000020 }, + { 0x00830000, 0x000000c3 }, + { 0x00930000, 0x000000c3 }, + { 0x01270700, 0x00000000 }, +}; + +static bool rt298_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT298_GET_HP_SENSE: + case RT298_GET_MIC1_SENSE: + case RT298_PROC_COEF: + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): + return true; + default: + return true; + } + + +} + +static bool rt298_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT298_GET_HP_SENSE: + case RT298_GET_MIC1_SENSE: + case RT298_SET_AUDIO_POWER: + case RT298_SET_HPO_POWER: + case RT298_SET_SPK_POWER: + case RT298_SET_DMIC1_POWER: + case RT298_SPK_MUX: + case RT298_HPO_MUX: + case RT298_ADC0_MUX: + case RT298_ADC1_MUX: + case RT298_SET_MIC1: + case RT298_SET_PIN_HPO: + case RT298_SET_PIN_SPK: + case RT298_SET_PIN_DMIC1: + case RT298_SPK_EAPD: + case RT298_SET_AMP_GAIN_HPO: + case RT298_SET_DMIC2_DEFAULT: + case RT298_DACL_GAIN: + case RT298_DACR_GAIN: + case RT298_ADCL_GAIN: + case RT298_ADCR_GAIN: + case RT298_MIC_GAIN: + case RT298_SPOL_GAIN: + case RT298_SPOR_GAIN: + case RT298_HPOL_GAIN: + case RT298_HPOR_GAIN: + case RT298_F_DAC_SWITCH: + case RT298_F_RECMIX_SWITCH: + case RT298_REC_MIC_SWITCH: + case RT298_REC_I2S_SWITCH: + case RT298_REC_LINE_SWITCH: + case RT298_REC_BEEP_SWITCH: + case RT298_DAC_FORMAT: + case RT298_ADC_FORMAT: + case RT298_COEF_INDEX: + case RT298_PROC_COEF: + case RT298_SET_AMP_GAIN_ADC_IN1: + case RT298_SET_AMP_GAIN_ADC_IN2: + case RT298_SET_POWER(RT298_DAC_OUT1): + case RT298_SET_POWER(RT298_DAC_OUT2): + case RT298_SET_POWER(RT298_ADC_IN1): + case RT298_SET_POWER(RT298_ADC_IN2): + case RT298_SET_POWER(RT298_DMIC2): + case RT298_SET_POWER(RT298_MIC1): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): + return true; + default: + return false; + } +} + +#ifdef CONFIG_PM +static void rt298_index_sync(struct snd_soc_codec *codec) +{ + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < INDEX_CACHE_SIZE; i++) { + snd_soc_write(codec, rt298->index_cache[i].reg, + rt298->index_cache[i].def); + } +} +#endif + +static int rt298_support_power_controls[] = { + RT298_DAC_OUT1, + RT298_DAC_OUT2, + RT298_ADC_IN1, + RT298_ADC_IN2, + RT298_MIC1, + RT298_DMIC1, + RT298_DMIC2, + RT298_SPK_OUT, + RT298_HP_OUT, +}; +#define RT298_POWER_REG_LEN ARRAY_SIZE(rt298_support_power_controls) + +static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic) +{ + struct snd_soc_dapm_context *dapm; + unsigned int val, buf; + + *hp = false; + *mic = false; + + if (!rt298->codec) + return -EINVAL; + + dapm = snd_soc_codec_get_dapm(rt298->codec); + + if (rt298->pdata.cbj_en) { + regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf); + *hp = buf & 0x80000000; + if (*hp == rt298->is_hp_in) + return -1; + rt298->is_hp_in = *hp; + if (*hp) { + /* power on HV,VERF */ + regmap_update_bits(rt298->regmap, + RT298_DC_GAIN, 0x200, 0x200); + + snd_soc_dapm_force_enable_pin(dapm, "HV"); + snd_soc_dapm_force_enable_pin(dapm, "VREF"); + /* power LDO1 */ + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + + regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24); + msleep(50); + + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xfcc0, 0xd400); + msleep(300); + regmap_read(rt298->regmap, RT298_CBJ_CTRL2, &val); + + if (0x0070 == (val & 0x0070)) { + *mic = true; + } else { + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xfcc0, 0xe400); + msleep(300); + regmap_read(rt298->regmap, + RT298_CBJ_CTRL2, &val); + if (0x0070 == (val & 0x0070)) + *mic = true; + else + *mic = false; + } + regmap_update_bits(rt298->regmap, + RT298_DC_GAIN, 0x200, 0x0); + + } else { + *mic = false; + regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20); + } + } else { + regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf); + *hp = buf & 0x80000000; + regmap_read(rt298->regmap, RT298_GET_MIC1_SENSE, &buf); + *mic = buf & 0x80000000; + } + + snd_soc_dapm_disable_pin(dapm, "HV"); + snd_soc_dapm_disable_pin(dapm, "VREF"); + if (!*hp) + snd_soc_dapm_disable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + + pr_debug("*hp = %d *mic = %d\n", *hp, *mic); + + return 0; +} + +static void rt298_jack_detect_work(struct work_struct *work) +{ + struct rt298_priv *rt298 = + container_of(work, struct rt298_priv, jack_detect_work.work); + int status = 0; + bool hp = false; + bool mic = false; + + if (rt298_jack_detect(rt298, &hp, &mic) < 0) + return; + + if (hp == true) + status |= SND_JACK_HEADPHONE; + + if (mic == true) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt298->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); +} + +int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) +{ + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + rt298->jack = jack; + + /* Send an initial empty report */ + snd_soc_jack_report(rt298->jack, 0, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + return 0; +} +EXPORT_SYMBOL_GPL(rt298_mic_detect); + +static int is_mclk_mode(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + if (rt298->clk_id == RT298_SCLK_S_MCLK) + return 1; + else + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt298_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT298_DACL_GAIN, + RT298_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT298_ADCL_GAIN, + RT298_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_SINGLE_TLV("AMIC Volume", RT298_MIC_GAIN, + 0, 0x3, 0, mic_vol_tlv), + SOC_DOUBLE_R("Speaker Playback Switch", RT298_SPOL_GAIN, + RT298_SPOR_GAIN, RT298_MUTE_SFT, 1, 1), +}; + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt298_front_mix[] = { + SOC_DAPM_SINGLE("DAC Switch", RT298_F_DAC_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIX Switch", RT298_F_RECMIX_SWITCH, + RT298_MUTE_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt298_rec_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", RT298_REC_MIC_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("I2S Switch", RT298_REC_I2S_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Line1 Switch", RT298_REC_LINE_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Beep Switch", RT298_REC_BEEP_SWITCH, + RT298_MUTE_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spo_enable_control = + SOC_DAPM_SINGLE("Switch", RT298_SET_PIN_SPK, + RT298_SET_PIN_SFT, 1, 0); + +static const struct snd_kcontrol_new hpol_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOL_GAIN, + RT298_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpor_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOR_GAIN, + RT298_MUTE_SFT, 1, 1); + +/* ADC0 source */ +static const char * const rt298_adc_src[] = { + "Mic", "RECMIX", "Dmic" +}; + +static const int rt298_adc_values[] = { + 0, 4, 5, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt298_adc0_enum, RT298_ADC0_MUX, RT298_ADC_SEL_SFT, + RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values); + +static const struct snd_kcontrol_new rt298_adc0_mux = + SOC_DAPM_ENUM("ADC 0 source", rt298_adc0_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt298_adc1_enum, RT298_ADC1_MUX, RT298_ADC_SEL_SFT, + RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values); + +static const struct snd_kcontrol_new rt298_adc1_mux = + SOC_DAPM_ENUM("ADC 1 source", rt298_adc1_enum); + +static const char * const rt298_dac_src[] = { + "Front", "Surround" +}; +/* HP-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt298_hpo_enum, RT298_HPO_MUX, + 0, rt298_dac_src); + +static const struct snd_kcontrol_new rt298_hpo_mux = +SOC_DAPM_ENUM("HPO source", rt298_hpo_enum); + +/* SPK-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt298_spo_enum, RT298_SPK_MUX, + 0, rt298_dac_src); + +static const struct snd_kcontrol_new rt298_spo_mux = +SOC_DAPM_ENUM("SPO source", rt298_spo_enum); + +static int rt298_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, + RT298_SPK_EAPD, RT298_SET_EAPD_HIGH); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, + RT298_SPK_EAPD, RT298_SET_EAPD_LOW); + break; + + default: + return 0; + } + + return 0; +} + +static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0x20); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0); + break; + default: + return 0; + } + + return 0; +} + +static int rt298_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int nid; + + nid = (w->reg >> 20) & 0xff; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), + 0x7080, 0x7000); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), + 0x7080, 0x7080); + break; + default: + return 0; + } + + return 0; +} + +static int rt298_mic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + RT298_A_BIAS_CTRL3, 0xc000, 0x8000); + snd_soc_update_bits(codec, + RT298_A_BIAS_CTRL2, 0xc000, 0x8000); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + RT298_A_BIAS_CTRL3, 0xc000, 0x0000); + snd_soc_update_bits(codec, + RT298_A_BIAS_CTRL2, 0xc000, 0x0000); + break; + default: + return 0; + } + + return 0; +} + +static int rt298_vref_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + RT298_CBJ_CTRL1, 0x0400, 0x0000); + mdelay(50); + break; + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1, + 12, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1, + 0, 1, rt298_vref_event, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT298_POWER_CTRL2, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VREF1", 1, RT298_POWER_CTRL2, + 4, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LV", 2, RT298_POWER_CTRL1, + 13, 1, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("MCLK MODE", RT298_PLL_CTRL1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM, + 0, 0, rt298_mic1_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("Beep"), + + /* DMIC */ + SND_SOC_DAPM_PGA_E("DMIC1", RT298_SET_POWER(RT298_DMIC1), 0, 1, + NULL, 0, rt298_set_dmic1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA("DMIC2", RT298_SET_POWER(RT298_DMIC2), 0, 1, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0, + rt298_rec_mix, ARRAY_SIZE(rt298_rec_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT298_SET_POWER(RT298_ADC_IN1), 0, 1, + &rt298_adc0_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT298_SET_POWER(RT298_ADC_IN2), 0, 1, + &rt298_adc1_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* Output Mux */ + SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt298_spo_mux), + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt298_hpo_mux), + + SND_SOC_DAPM_SUPPLY("HP Power", RT298_SET_PIN_HPO, + RT298_SET_PIN_SFT, 0, NULL, 0), + + /* Output Mixer */ + SND_SOC_DAPM_MIXER("Front", RT298_SET_POWER(RT298_DAC_OUT1), 0, 1, + rt298_front_mix, ARRAY_SIZE(rt298_front_mix)), + SND_SOC_DAPM_PGA("Surround", RT298_SET_POWER(RT298_DAC_OUT2), 0, 1, + NULL, 0), + + /* Output Pga */ + SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0, + &spo_enable_control, rt298_spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0, + &hpol_enable_control), + SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0, + &hpor_enable_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_OUTPUT("HPO Pin"), + SND_SOC_DAPM_OUTPUT("SPDIF"), +}; + +static const struct snd_soc_dapm_route rt298_dapm_routes[] = { + + {"ADC 0", NULL, "MCLK MODE", is_mclk_mode}, + {"ADC 1", NULL, "MCLK MODE", is_mclk_mode}, + {"Front", NULL, "MCLK MODE", is_mclk_mode}, + {"Surround", NULL, "MCLK MODE", is_mclk_mode}, + + {"HP Power", NULL, "LDO1"}, + {"HP Power", NULL, "LDO2"}, + {"HP Power", NULL, "LV"}, + {"HP Power", NULL, "VREF1"}, + {"HP Power", NULL, "BG_MBIAS"}, + + {"MIC1", NULL, "LDO1"}, + {"MIC1", NULL, "LDO2"}, + {"MIC1", NULL, "HV"}, + {"MIC1", NULL, "LV"}, + {"MIC1", NULL, "VREF"}, + {"MIC1", NULL, "VREF1"}, + {"MIC1", NULL, "BG_MBIAS"}, + {"MIC1", NULL, "MIC1 Input Buffer"}, + + {"SPO", NULL, "LDO1"}, + {"SPO", NULL, "LDO2"}, + {"SPO", NULL, "HV"}, + {"SPO", NULL, "LV"}, + {"SPO", NULL, "VREF"}, + {"SPO", NULL, "VREF1"}, + {"SPO", NULL, "BG_MBIAS"}, + + {"DMIC1", NULL, "DMIC1 Pin"}, + {"DMIC2", NULL, "DMIC2 Pin"}, + {"DMIC1", NULL, "DMIC Receiver"}, + {"DMIC2", NULL, "DMIC Receiver"}, + + {"RECMIX", "Beep Switch", "Beep"}, + {"RECMIX", "Line1 Switch", "LINE1"}, + {"RECMIX", "Mic1 Switch", "MIC1"}, + + {"ADC 0 Mux", "Dmic", "DMIC1"}, + {"ADC 0 Mux", "RECMIX", "RECMIX"}, + {"ADC 0 Mux", "Mic", "MIC1"}, + {"ADC 1 Mux", "Dmic", "DMIC2"}, + {"ADC 1 Mux", "RECMIX", "RECMIX"}, + {"ADC 1 Mux", "Mic", "MIC1"}, + + {"ADC 0", NULL, "ADC 0 Mux"}, + {"ADC 1", NULL, "ADC 1 Mux"}, + + {"AIF1TX", NULL, "ADC 0"}, + {"AIF2TX", NULL, "ADC 1"}, + + {"DAC 0", NULL, "AIF1RX"}, + {"DAC 1", NULL, "AIF2RX"}, + + {"Front", "DAC Switch", "DAC 0"}, + {"Front", "RECMIX Switch", "RECMIX"}, + + {"Surround", NULL, "DAC 1"}, + + {"SPK Mux", "Front", "Front"}, + {"SPK Mux", "Surround", "Surround"}, + + {"HPO Mux", "Front", "Front"}, + {"HPO Mux", "Surround", "Surround"}, + + {"SPO", "Switch", "SPK Mux"}, + {"HPO L", "Switch", "HPO Mux"}, + {"HPO R", "Switch", "HPO Mux"}, + {"HPO L", NULL, "HP Power"}, + {"HPO R", NULL, "HP Power"}, + + {"SPOL", NULL, "SPO"}, + {"SPOR", NULL, "SPO"}, + {"HPO Pin", NULL, "HPO L"}, + {"HPO Pin", NULL, "HPO R"}, +}; + +static int rt298_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 rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + int d_len_code; + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + case 44100: + case 48000: + break; + default: + dev_err(codec->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + switch (rt298->sys_clk) { + case 12288000: + case 24576000: + if (params_rate(params) != 48000) { + dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt298->sys_clk); + return -EINVAL; + } + break; + case 11289600: + case 22579200: + if (params_rate(params) != 44100) { + dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt298->sys_clk); + return -EINVAL; + } + break; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(codec->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + d_len_code = 0; + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 16: + d_len_code = 0; + val |= (0x1 << 4); + break; + case 32: + d_len_code = 2; + val |= (0x4 << 4); + break; + case 20: + d_len_code = 1; + val |= (0x2 << 4); + break; + case 24: + d_len_code = 2; + val |= (0x3 << 4); + break; + case 8: + d_len_code = 3; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x0018, d_len_code << 3); + dev_dbg(codec->dev, "format val = 0x%x\n", val); + + snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x407f, val); + snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x407f, val); + + return 0; +} + +static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x800, 0x800); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x800, 0x0); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x300, 0x0); + break; + case SND_SOC_DAIFMT_LEFT_J: + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x300, 0x1 << 8); + break; + case SND_SOC_DAIFMT_DSP_A: + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x300, 0x2 << 8); + break; + case SND_SOC_DAIFMT_DSP_B: + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x300, 0x3 << 8); + break; + default: + return -EINVAL; + } + /* bit 15 Stream Type 0:PCM 1:Non-PCM */ + snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x8000, 0); + snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x8000, 0); + + return 0; +} + +static int rt298_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq); + + if (RT298_SCLK_S_MCLK == clk_id) { + snd_soc_update_bits(codec, + RT298_I2S_CTRL2, 0x0100, 0x0); + snd_soc_update_bits(codec, + RT298_PLL_CTRL1, 0x20, 0x20); + } else { + snd_soc_update_bits(codec, + RT298_I2S_CTRL2, 0x0100, 0x0100); + snd_soc_update_bits(codec, + RT298_PLL_CTRL, 0x4, 0x4); + snd_soc_update_bits(codec, + RT298_PLL_CTRL1, 0x20, 0x0); + } + + switch (freq) { + case 19200000: + if (RT298_SCLK_S_MCLK == clk_id) { + dev_err(codec->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_update_bits(codec, + RT298_I2S_CTRL2, 0x40, 0x40); + break; + case 24000000: + if (RT298_SCLK_S_MCLK == clk_id) { + dev_err(codec->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_update_bits(codec, + RT298_I2S_CTRL2, 0x40, 0x0); + break; + case 12288000: + case 11289600: + snd_soc_update_bits(codec, + RT298_I2S_CTRL2, 0x8, 0x0); + snd_soc_update_bits(codec, + RT298_CLK_DIV, 0xfc1e, 0x0004); + break; + case 24576000: + case 22579200: + snd_soc_update_bits(codec, + RT298_I2S_CTRL2, 0x8, 0x8); + snd_soc_update_bits(codec, + RT298_CLK_DIV, 0xfc1e, 0x5406); + break; + default: + dev_err(codec->dev, "Unsupported system clock\n"); + return -EINVAL; + } + + rt298->sys_clk = freq; + rt298->clk_id = clk_id; + + return 0; +} + +static int rt298_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_codec *codec = dai->codec; + + dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio); + if (50 == ratio) + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x1000, 0x1000); + else + snd_soc_update_bits(codec, + RT298_I2S_CTRL1, 0x1000, 0x0); + + + return 0; +} + +static int rt298_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == + snd_soc_codec_get_bias_level(codec)) { + snd_soc_write(codec, + RT298_SET_AUDIO_POWER, AC_PWRST_D0); + snd_soc_update_bits(codec, 0x0d, 0x200, 0x200); + snd_soc_update_bits(codec, 0x52, 0x80, 0x0); + mdelay(20); + snd_soc_update_bits(codec, 0x0d, 0x200, 0x0); + snd_soc_update_bits(codec, 0x52, 0x80, 0x80); + } + break; + + case SND_SOC_BIAS_ON: + mdelay(30); + snd_soc_update_bits(codec, + RT298_CBJ_CTRL1, 0x0400, 0x0400); + + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_write(codec, + RT298_SET_AUDIO_POWER, AC_PWRST_D3); + snd_soc_update_bits(codec, + RT298_CBJ_CTRL1, 0x0400, 0x0000); + break; + + default: + break; + } + + return 0; +} + +static irqreturn_t rt298_irq(int irq, void *data) +{ + struct rt298_priv *rt298 = data; + bool hp = false; + bool mic = false; + int ret, status = 0; + + ret = rt298_jack_detect(rt298, &hp, &mic); + + /* Clear IRQ */ + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1); + + if (ret == 0) { + if (hp == true) + status |= SND_JACK_HEADPHONE; + + if (mic == true) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt298->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + pm_wakeup_event(&rt298->i2c->dev, 300); + } + + return IRQ_HANDLED; +} + +static int rt298_probe(struct snd_soc_codec *codec) +{ + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + rt298->codec = codec; + + if (rt298->i2c->irq) { + regmap_update_bits(rt298->regmap, + RT298_IRQ_CTRL, 0x2, 0x2); + + INIT_DELAYED_WORK(&rt298->jack_detect_work, + rt298_jack_detect_work); + schedule_delayed_work(&rt298->jack_detect_work, + msecs_to_jiffies(1250)); + } + + return 0; +} + +static int rt298_remove(struct snd_soc_codec *codec) +{ + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + cancel_delayed_work_sync(&rt298->jack_detect_work); + + return 0; +} + +#ifdef CONFIG_PM +static int rt298_suspend(struct snd_soc_codec *codec) +{ + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + rt298->is_hp_in = -1; + regcache_cache_only(rt298->regmap, true); + regcache_mark_dirty(rt298->regmap); + + return 0; +} + +static int rt298_resume(struct snd_soc_codec *codec) +{ + struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt298->regmap, false); + rt298_index_sync(codec); + regcache_sync(rt298->regmap); + + return 0; +} +#else +#define rt298_suspend NULL +#define rt298_resume NULL +#endif + +#define RT298_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT298_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt298_aif_dai_ops = { + .hw_params = rt298_hw_params, + .set_fmt = rt298_set_dai_fmt, + .set_sysclk = rt298_set_dai_sysclk, + .set_bclk_ratio = rt298_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt298_dai[] = { + { + .name = "rt298-aif1", + .id = RT298_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .ops = &rt298_aif_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "rt298-aif2", + .id = RT298_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .ops = &rt298_aif_dai_ops, + .symmetric_rates = 1, + }, + +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt298 = { + .probe = rt298_probe, + .remove = rt298_remove, + .suspend = rt298_suspend, + .resume = rt298_resume, + .set_bias_level = rt298_set_bias_level, + .idle_bias_off = true, + .controls = rt298_snd_controls, + .num_controls = ARRAY_SIZE(rt298_snd_controls), + .dapm_widgets = rt298_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets), + .dapm_routes = rt298_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes), +}; + +static const struct regmap_config rt298_regmap = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x02370100, + .volatile_reg = rt298_volatile_register, + .readable_reg = rt298_readable_register, + .reg_write = rl6347a_hw_write, + .reg_read = rl6347a_hw_read, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt298_reg, + .num_reg_defaults = ARRAY_SIZE(rt298_reg), +}; + +static const struct i2c_device_id rt298_i2c_id[] = { + {"rt298", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt298_i2c_id); + +static const struct acpi_device_id rt298_acpi_match[] = { + { "INT343A", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); + +static int rt298_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt298_priv *rt298; + struct device *dev = &i2c->dev; + const struct acpi_device_id *acpiid; + int i, ret; + + rt298 = devm_kzalloc(&i2c->dev, sizeof(*rt298), + GFP_KERNEL); + if (NULL == rt298) + return -ENOMEM; + + rt298->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt298_regmap); + if (IS_ERR(rt298->regmap)) { + ret = PTR_ERR(rt298->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt298->regmap, + RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret); + if (ret != RT298_VENDOR_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt298\n", ret); + return -ENODEV; + } + + rt298->index_cache = rt298_index_def; + rt298->index_cache_size = INDEX_CACHE_SIZE; + rt298->i2c = i2c; + i2c_set_clientdata(i2c, rt298); + + /* restore codec default */ + for (i = 0; i < INDEX_CACHE_SIZE; i++) + regmap_write(rt298->regmap, rt298->index_cache[i].reg, + rt298->index_cache[i].def); + for (i = 0; i < ARRAY_SIZE(rt298_reg); i++) + regmap_write(rt298->regmap, rt298_reg[i].reg, + rt298_reg[i].def); + + if (pdata) + rt298->pdata = *pdata; + + /* enable jack combo mode on supported devices */ + acpiid = acpi_match_device(dev->driver->acpi_match_table, dev); + if (acpiid) { + rt298->pdata = *(struct rt298_platform_data *) + acpiid->driver_data; + } + + /* VREF Charging */ + regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80); + regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860); + /* Vref2 */ + regmap_update_bits(rt298->regmap, 0x08, 0x20, 0x20); + + regmap_write(rt298->regmap, RT298_SET_AUDIO_POWER, AC_PWRST_D3); + + for (i = 0; i < RT298_POWER_REG_LEN; i++) + regmap_write(rt298->regmap, + RT298_SET_POWER(rt298_support_power_controls[i]), + AC_PWRST_D1); + + if (!rt298->pdata.cbj_en) { + regmap_write(rt298->regmap, RT298_CBJ_CTRL2, 0x0000); + regmap_write(rt298->regmap, RT298_MIC1_DET_CTRL, 0x0816); + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xf000, 0xb000); + } else { + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xf000, 0x5000); + } + + mdelay(10); + + if (!rt298->pdata.gpio2_en) + regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x4000); + else + regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0); + + mdelay(10); + + regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000); + regmap_update_bits(rt298->regmap, + RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); + rt298->is_hp_in = -1; + + if (rt298->i2c->irq) { + ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298); + if (ret != 0) { + dev_err(&i2c->dev, + "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt298, + rt298_dai, ARRAY_SIZE(rt298_dai)); + + return ret; +} + +static int rt298_i2c_remove(struct i2c_client *i2c) +{ + struct rt298_priv *rt298 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt298); + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + + +static struct i2c_driver rt298_i2c_driver = { + .driver = { + .name = "rt298", + .acpi_match_table = ACPI_PTR(rt298_acpi_match), + }, + .probe = rt298_i2c_probe, + .remove = rt298_i2c_remove, + .id_table = rt298_i2c_id, +}; + +module_i2c_driver(rt298_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT298 driver"); +MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h new file mode 100644 index 0000000..31da162 --- /dev/null +++ b/sound/soc/codecs/rt298.h @@ -0,0 +1,206 @@ +/* + * rt298.h -- RT298 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu <johnnyhsu@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT298_H__ +#define __RT298_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RT298_AUDIO_FUNCTION_GROUP 0x01 +#define RT298_DAC_OUT1 0x02 +#define RT298_DAC_OUT2 0x03 +#define RT298_DIG_CVT 0x06 +#define RT298_ADC_IN1 0x09 +#define RT298_ADC_IN2 0x08 +#define RT298_MIXER_IN 0x0b +#define RT298_MIXER_OUT1 0x0c +#define RT298_MIXER_OUT2 0x0d +#define RT298_DMIC1 0x12 +#define RT298_DMIC2 0x13 +#define RT298_SPK_OUT 0x14 +#define RT298_MIC1 0x18 +#define RT298_LINE1 0x1a +#define RT298_BEEP 0x1d +#define RT298_SPDIF 0x1e +#define RT298_VENDOR_REGISTERS 0x20 +#define RT298_HP_OUT 0x21 +#define RT298_MIXER_IN1 0x22 +#define RT298_MIXER_IN2 0x23 + +#define RT298_SET_PIN_SFT 6 +#define RT298_SET_PIN_ENABLE 0x40 +#define RT298_SET_PIN_DISABLE 0 +#define RT298_SET_EAPD_HIGH 0x2 +#define RT298_SET_EAPD_LOW 0 + +#define RT298_MUTE_SFT 7 + +/* Verb commands */ +#define RT298_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM) +#define RT298_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0) +#define RT298_SET_AUDIO_POWER RT298_SET_POWER(RT298_AUDIO_FUNCTION_GROUP) +#define RT298_SET_HPO_POWER RT298_SET_POWER(RT298_HP_OUT) +#define RT298_SET_SPK_POWER RT298_SET_POWER(RT298_SPK_OUT) +#define RT298_SET_DMIC1_POWER RT298_SET_POWER(RT298_DMIC1) +#define RT298_SPK_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_SPK_OUT, 0) +#define RT298_HPO_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_HP_OUT, 0) +#define RT298_ADC0_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN1, 0) +#define RT298_ADC1_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN2, 0) +#define RT298_SET_MIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_MIC1, 0) +#define RT298_SET_PIN_HPO\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_HP_OUT, 0) +#define RT298_SET_PIN_SPK\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPK_OUT, 0) +#define RT298_SET_PIN_DMIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_DMIC1, 0) +#define RT298_SET_PIN_SPDIF\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPDIF, 0) +#define RT298_SET_PIN_DIG_CVT\ + VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT298_DIG_CVT, 0) +#define RT298_SPK_EAPD\ + VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT298_SPK_OUT, 0) +#define RT298_SET_AMP_GAIN_HPO\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0) +#define RT298_SET_AMP_GAIN_ADC_IN1\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0) +#define RT298_SET_AMP_GAIN_ADC_IN2\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN2, 0) +#define RT298_GET_HP_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_HP_OUT, 0) +#define RT298_GET_MIC1_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_MIC1, 0) +#define RT298_SET_DMIC2_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_DMIC2, 0) +#define RT298_SET_SPDIF_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_SPDIF, 0) +#define RT298_DACL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0xa000) +#define RT298_DACR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0x9000) +#define RT298_ADCL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x6000) +#define RT298_ADCR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x5000) +#define RT298_MIC_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIC1, 0x7000) +#define RT298_SPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0xa000) +#define RT298_SPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0x9000) +#define RT298_HPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0xa000) +#define RT298_HPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0x9000) +#define RT298_F_DAC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7000) +#define RT298_F_RECMIX_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7100) +#define RT298_REC_MIC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7000) +#define RT298_REC_I2S_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7100) +#define RT298_REC_LINE_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7200) +#define RT298_REC_BEEP_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7300) +#define RT298_DAC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_DAC_OUT1, 0) +#define RT298_ADC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_ADC_IN1, 0) +#define RT298_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0) +#define RT298_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0) + +/* Index registers */ +#define RT298_A_BIAS_CTRL1 0x01 +#define RT298_A_BIAS_CTRL2 0x02 +#define RT298_POWER_CTRL1 0x03 +#define RT298_A_BIAS_CTRL3 0x04 +#define RT298_POWER_CTRL2 0x08 +#define RT298_I2S_CTRL1 0x09 +#define RT298_I2S_CTRL2 0x0a +#define RT298_CLK_DIV 0x0b +#define RT298_DC_GAIN 0x0d +#define RT298_POWER_CTRL3 0x0f +#define RT298_MIC1_DET_CTRL 0x19 +#define RT298_MISC_CTRL1 0x20 +#define RT298_IRQ_CTRL 0x33 +#define RT298_WIND_FILTER_CTRL 0x46 +#define RT298_PLL_CTRL1 0x49 +#define RT298_CBJ_CTRL1 0x4f +#define RT298_CBJ_CTRL2 0x50 +#define RT298_PLL_CTRL 0x63 +#define RT298_DEPOP_CTRL1 0x66 +#define RT298_DEPOP_CTRL2 0x67 +#define RT298_DEPOP_CTRL3 0x68 +#define RT298_DEPOP_CTRL4 0x69 + +/* SPDIF (0x06) */ +#define RT298_SPDIF_SEL_SFT 0 +#define RT298_SPDIF_SEL_PCM0 0 +#define RT298_SPDIF_SEL_PCM1 1 +#define RT298_SPDIF_SEL_SPOUT 2 +#define RT298_SPDIF_SEL_PP 3 + +/* RECMIX (0x0b) */ +#define RT298_M_REC_BEEP_SFT 0 +#define RT298_M_REC_LINE1_SFT 1 +#define RT298_M_REC_MIC1_SFT 2 +#define RT298_M_REC_I2S_SFT 3 + +/* Front (0x0c) */ +#define RT298_M_FRONT_DAC_SFT 0 +#define RT298_M_FRONT_REC_SFT 1 + +/* SPK-OUT (0x14) */ +#define RT298_M_SPK_MUX_SFT 14 +#define RT298_SPK_SEL_MASK 0x1 +#define RT298_SPK_SEL_SFT 0 +#define RT298_SPK_SEL_F 0 +#define RT298_SPK_SEL_S 1 + +/* HP-OUT (0x21) */ +#define RT298_M_HP_MUX_SFT 14 +#define RT298_HP_SEL_MASK 0x1 +#define RT298_HP_SEL_SFT 0 +#define RT298_HP_SEL_F 0 +#define RT298_HP_SEL_S 1 + +/* ADC (0x22) (0x23) */ +#define RT298_ADC_SEL_MASK 0x7 +#define RT298_ADC_SEL_SFT 0 +#define RT298_ADC_SEL_SURR 0 +#define RT298_ADC_SEL_FRONT 1 +#define RT298_ADC_SEL_DMIC 2 +#define RT298_ADC_SEL_BEEP 4 +#define RT298_ADC_SEL_LINE1 5 +#define RT298_ADC_SEL_I2S 6 +#define RT298_ADC_SEL_MIC1 7 + +#define RT298_SCLK_S_MCLK 0 +#define RT298_SCLK_S_PLL 1 + +enum { + RT298_AIF1, + RT298_AIF2, + RT298_AIFS, +}; + +int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack); + +#endif /* __RT298_H__ */ + diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 058167c..1be2bab 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -174,16 +174,15 @@ static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95625, 375, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52}dB */ -static unsigned int mic_bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const DECLARE_TLV_DB_RANGE(mic_bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), - 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); static int rt5631_dmic_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1725,7 +1724,6 @@ static int rt5631_i2c_remove(struct i2c_client *client) static struct i2c_driver rt5631_i2c_driver = { .driver = { .name = "rt5631", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, .probe = rt5631_i2c_probe, diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 9bc78e5..e1ceeb8 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5640_ranges[] = { .window_len = 0x1, }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5640_PR_BASE + 0x3d, 0x3600}, {RT5640_PR_BASE + 0x12, 0x0aa8}, {RT5640_PR_BASE + 0x14, 0x0aaa}, @@ -347,16 +347,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ -static unsigned int bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const DECLARE_TLV_DB_RANGE(bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), - 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); /* Interface data select */ static const char * const rt5640_data_select[] = { @@ -459,10 +458,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); - int idx = -EINVAL; - - idx = rl6231_calc_dmic_clk(rt5640->sysclk); + int idx, rate; + rate = rt5640->sysclk / rl6231_get_pre_div(rt5640->regmap, + RT5640_ADDA_CLK1, RT5640_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); if (idx < 0) dev_err(codec->dev, "Failed to set DMIC clock\n"); else @@ -984,6 +984,35 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5640_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power_on(codec); + snd_soc_update_bits(codec, RT5640_PWR_ANLG1, + RT5640_PWR_LM, RT5640_PWR_LM); + snd_soc_update_bits(codec, RT5640_OUTPUT, + RT5640_L_MUTE | RT5640_R_MUTE, 0); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5640_OUTPUT, + RT5640_L_MUTE | RT5640_R_MUTE, + RT5640_L_MUTE | RT5640_R_MUTE); + snd_soc_update_bits(codec, RT5640_PWR_ANLG1, + RT5640_PWR_LM, 0); + break; + + default: + return 0; + } + + return 0; +} + static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1179,13 +1208,16 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)), SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)), - SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0, + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)), SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5640_hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, + rt5640_lout_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1, RT5640_PWR_HP_L_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1, @@ -1500,8 +1532,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"HP R Playback", "Switch", "HP Amp"}, {"HPOL", NULL, "HP L Playback"}, {"HPOR", NULL, "HP R Playback"}, - {"LOUTL", NULL, "LOUT MIX"}, - {"LOUTR", NULL, "LOUT MIX"}, + + {"LOUT amp", NULL, "LOUT MIX"}, + {"LOUTL", NULL, "LOUT amp"}, + {"LOUTR", NULL, "LOUT amp"}, }; static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { @@ -2207,7 +2241,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); if (val != RT5640_DEVICE_ID) { dev_err(&i2c->dev, - "Device with ID register %x is not rt5640/39\n", val); + "Device with ID register %#x is not rt5640/39\n", val); return -ENODEV; } @@ -2242,7 +2276,6 @@ static int rt5640_i2c_remove(struct i2c_client *i2c) static struct i2c_driver rt5640_i2c_driver = { .driver = { .name = "rt5640", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(rt5640_acpi_match), .of_match_table = of_match_ptr(rt5640_of_match), }, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 961bd7e..4972bf3 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -21,6 +21,7 @@ #include <linux/gpio/consumer.h> #include <linux/acpi.h> #include <linux/dmi.h> +#include <linux/regulator/consumer.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -54,7 +55,7 @@ static const struct regmap_range_cfg rt5645_ranges[] = { }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5645_PR_BASE + 0x3d, 0x3600}, {RT5645_PR_BASE + 0x1c, 0xfd20}, {RT5645_PR_BASE + 0x20, 0x611f}, @@ -63,7 +64,7 @@ static const struct reg_default init_list[] = { }; #define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list) -static const struct reg_default rt5650_init_list[] = { +static const struct reg_sequence rt5650_init_list[] = { {0xf6, 0x0100}, }; @@ -223,6 +224,39 @@ static const struct reg_default rt5645_reg[] = { { 0xff, 0x6308 }, }; +static const char *const rt5645_supply_names[] = { + "avdd", + "cpvdd", +}; + +struct rt5645_priv { + struct snd_soc_codec *codec; + struct rt5645_platform_data pdata; + struct regmap *regmap; + struct i2c_client *i2c; + struct gpio_desc *gpiod_hp_det; + struct snd_soc_jack *hp_jack; + struct snd_soc_jack *mic_jack; + struct snd_soc_jack *btn_jack; + struct delayed_work jack_detect_work; + struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; + + int codec_type; + int sysclk; + int sysclk_src; + int lrck[RT5645_AIFS]; + int bclk[RT5645_AIFS]; + int master[RT5645_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; + bool en_button_func; + bool hp_on; +}; + static int rt5645_reset(struct snd_soc_codec *codec) { return snd_soc_write(codec, RT5645_RESET, 0); @@ -360,6 +394,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg) case RT5645_DEPOP_M1: case RT5645_DEPOP_M2: case RT5645_DEPOP_M3: + case RT5645_CHARGE_PUMP: case RT5645_MICBIAS: case RT5645_A_JD_CTRL1: case RT5645_VAD_CTRL4: @@ -424,16 +459,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ -static unsigned int bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const DECLARE_TLV_DB_RANGE(bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), - 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ @@ -510,10 +544,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - int idx = -EINVAL; - - idx = rl6231_calc_dmic_clk(rt5645->sysclk); + int idx, rate; + rate = rt5645->sysclk / rl6231_get_pre_div(rt5645->regmap, + RT5645_ADDA_CLK1, RT5645_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); if (idx < 0) dev_err(codec->dev, "Failed to set DMIC clock\n"); else @@ -1331,15 +1366,23 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) if (on) { if (hp_amp_power_count <= 0) { if (rt5645->codec_type == CODEC_TYPE_RT5650) { + snd_soc_write(codec, RT5645_DEPOP_M2, 0x3100); snd_soc_write(codec, RT5645_CHARGE_PUMP, 0x0e06); - snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_HP_DCC_INT1, 0x9f01); + msleep(20); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_CO_MASK, RT5645_HP_CO_EN); regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x3e, 0x7400); snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737); regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); + mdelay(5); + rt5645->hp_on = true; } else { /* depop parameters */ snd_soc_update_bits(codec, RT5645_DEPOP_M2, @@ -1553,6 +1596,27 @@ static int rt5645_bst2_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5650_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (rt5645->hp_on) { + msleep(100); + rt5645->hp_on = false; + } + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER, RT5645_PWR_LDO2_BIT, 0, NULL, 0), @@ -1697,15 +1761,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), /* IF1 2 Mux */ - SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5645_if1_adc1_in_mux), - SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5645_if1_adc2_in_mux), - SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5645_if1_adc3_in_mux), - SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM, - 0, 0, &rt5645_if1_adc_in_mux), - SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if2_adc_in_mux), @@ -1716,14 +1771,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, - &rt5645_if1_dac0_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, - &rt5645_if1_dac1_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, - &rt5645_if1_dac2_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, - &rt5645_if1_dac3_tdm_sel_mux), SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1854,6 +1901,26 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("PDM1R"), SND_SOC_DAPM_OUTPUT("SPOL"), SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_POST("DAPM_POST", rt5650_hp_event), +}; + +static const struct snd_soc_dapm_widget rt5645_specific_dapm_widgets[] = { + SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac3_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc1_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc3_in_mux), }; static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = { @@ -2642,7 +2709,7 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2, @@ -2686,94 +2753,15 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int rt5650_calibration(struct rt5645_priv *rt5645) -{ - int val, i; - int ret = -1; - - regcache_cache_bypass(rt5645->regmap, true); - regmap_write(rt5645->regmap, RT5645_RESET, 0); - regmap_write(rt5645->regmap, RT5645_GEN_CTRL3, 0x0800); - regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_CHOP_DAC_ADC, - 0x3600); - regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x25, 0x7000); - regmap_write(rt5645->regmap, RT5645_I2S1_SDP, 0x8008); - /* headset type */ - regmap_write(rt5645->regmap, RT5645_GEN_CTRL1, 0x2061); - regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006); - regmap_write(rt5645->regmap, RT5645_PWR_ANLG1, 0x2012); - regmap_write(rt5645->regmap, RT5645_PWR_MIXER, 0x0002); - regmap_write(rt5645->regmap, RT5645_PWR_VOL, 0x0020); - regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0); - regmap_write(rt5645->regmap, RT5645_IN1_CTRL1, 0x0006); - regmap_write(rt5645->regmap, RT5645_IN1_CTRL2, 0x1827); - regmap_write(rt5645->regmap, RT5645_IN1_CTRL2, 0x0827); - msleep(400); - /* Inline command */ - regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0001); - regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD2, 0xc000); - regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD1, 0x0008); - /* Calbration */ - regmap_write(rt5645->regmap, RT5645_GLB_CLK, 0x8000); - regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0000); - regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD2, 0xc000); - regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD1, 0x0008); - regmap_write(rt5645->regmap, RT5645_PWR_DIG2, 0x8800); - regmap_write(rt5645->regmap, RT5645_PWR_ANLG1, 0xe8fa); - regmap_write(rt5645->regmap, RT5645_PWR_ANLG2, 0x8c04); - regmap_write(rt5645->regmap, RT5645_DEPOP_M2, 0x3100); - regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); - regmap_write(rt5645->regmap, RT5645_BASS_BACK, 0x8a13); - regmap_write(rt5645->regmap, RT5645_GEN_CTRL3, 0x0820); - regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x000d); - /* Power on and Calbration */ - regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_HP_DCC_INT1, - 0x9f01); - msleep(200); - for (i = 0; i < 5; i++) { - regmap_read(rt5645->regmap, RT5645_PR_BASE + 0x7a, &val); - if (val != 0 && val != 0x3f3f) { - ret = 0; - break; - } - msleep(50); - } - pr_debug("%s: PR-7A = 0x%x\n", __func__, val); - - /* mute */ - regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x3e, 0x7400); - regmap_write(rt5645->regmap, RT5645_DEPOP_M3, 0x0737); - regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, - 0xfc00); - regmap_write(rt5645->regmap, RT5645_DEPOP_M2, 0x1140); - regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0000); - regmap_write(rt5645->regmap, RT5645_GEN_CTRL2, 0x4020); - regmap_write(rt5645->regmap, RT5645_PWR_ANLG2, 0x0006); - regmap_write(rt5645->regmap, RT5645_PWR_DIG2, 0x0000); - msleep(350); - - regcache_cache_bypass(rt5645->regmap, false); - - return ret; -} - static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, bool enable) { - struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (enable) { - snd_soc_dapm_mutex_lock(&codec->dapm); - snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, - "ADC L power"); - snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, - "ADC R power"); - snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, - "LDO2"); - snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync_unlocked(&codec->dapm); - snd_soc_dapm_mutex_unlock(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "ADC L power"); + snd_soc_dapm_force_enable_pin(dapm, "ADC R power"); + snd_soc_dapm_sync(dapm); snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x8); @@ -2786,36 +2774,26 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0); snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0); - snd_soc_dapm_mutex_lock(&codec->dapm); - snd_soc_dapm_disable_pin_unlocked(&codec->dapm, - "ADC L power"); - snd_soc_dapm_disable_pin_unlocked(&codec->dapm, - "ADC R power"); - if (rt5645->pdata.jd_mode == 0) - snd_soc_dapm_disable_pin_unlocked(&codec->dapm, - "LDO2"); - snd_soc_dapm_disable_pin_unlocked(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync_unlocked(&codec->dapm); - snd_soc_dapm_mutex_unlock(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "ADC L power"); + snd_soc_dapm_disable_pin(dapm, "ADC R power"); + snd_soc_dapm_sync(dapm); } } static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); unsigned int val; if (jack_insert) { regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006); - if (codec->component.card->instantiated) { - /* for jack type detect */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); - snd_soc_dapm_force_enable_pin(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); - } else { + /* for jack type detect */ + snd_soc_dapm_force_enable_pin(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + if (!dapm->card->instantiated) { /* Power up necessary bits for JD if dapm is not ready yet */ regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1, @@ -2828,14 +2806,15 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) } regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0); - regmap_write(rt5645->regmap, RT5645_IN1_CTRL1, 0x0006); - regmap_update_bits(rt5645->regmap, - RT5645_IN1_CTRL2, 0x1000, 0x1000); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, + RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); msleep(100); - regmap_update_bits(rt5645->regmap, - RT5645_IN1_CTRL2, 0x1000, 0x0000); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, 0); - msleep(450); + msleep(600); regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val); val &= 0x7; dev_dbg(codec->dev, "val = %d\n", val); @@ -2846,43 +2825,46 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) rt5645_enable_push_button_irq(codec, true); } } else { - if (codec->component.card->instantiated) { - snd_soc_dapm_disable_pin(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); - } else - regmap_update_bits(rt5645->regmap, - RT5645_PWR_VOL, RT5645_PWR_MIC_DET, 0); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; } + snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001); } else { /* jack out */ rt5645->jack_type = 0; + + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, + RT5645_CBJ_BST1_EN, 0); + if (rt5645->en_button_func) rt5645_enable_push_button_irq(codec, false); - else { - if (codec->component.card->instantiated) { - if (rt5645->pdata.jd_mode == 0) - snd_soc_dapm_disable_pin(&codec->dapm, - "LDO2"); - snd_soc_dapm_disable_pin(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); - } else { - if (rt5645->pdata.jd_mode == 0) - regmap_update_bits(rt5645->regmap, - RT5645_PWR_MIXER, - RT5645_PWR_LDO2, 0); - regmap_update_bits(rt5645->regmap, - RT5645_PWR_VOL, RT5645_PWR_MIC_DET, 0); - } - } + + if (rt5645->pdata.jd_mode == 0) + snd_soc_dapm_disable_pin(dapm, "LDO2"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); } return rt5645->jack_type; } -static int rt5645_irq_detection(struct rt5645_priv *rt5645); +static int rt5645_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1); + pr_debug("val=0x%x\n", val); + btn_type = val & 0xfff0; + snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val); + + return btn_type; +} + static irqreturn_t rt5645_irq(int irq, void *data); int rt5645_set_jack_detect(struct snd_soc_codec *codec, @@ -2913,38 +2895,10 @@ static void rt5645_jack_detect_work(struct work_struct *work) { struct rt5645_priv *rt5645 = container_of(work, struct rt5645_priv, jack_detect_work.work); - - rt5645_irq_detection(rt5645); -} - -static irqreturn_t rt5645_irq(int irq, void *data) -{ - struct rt5645_priv *rt5645 = data; - - queue_delayed_work(system_power_efficient_wq, - &rt5645->jack_detect_work, msecs_to_jiffies(250)); - - return IRQ_HANDLED; -} - -static int rt5645_button_detect(struct snd_soc_codec *codec) -{ - int btn_type, val; - - val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1); - pr_debug("val=0x%x\n", val); - btn_type = val & 0xfff0; - snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val); - - return btn_type; -} - -static int rt5645_irq_detection(struct rt5645_priv *rt5645) -{ int val, btn_type, gpio_state = 0, report = 0; if (!rt5645->codec) - return -EINVAL; + return; switch (rt5645->pdata.jd_mode) { case 0: /* Not using rt5645 JD */ @@ -2958,7 +2912,7 @@ static int rt5645_irq_detection(struct rt5645_priv *rt5645) report, SND_JACK_HEADPHONE); snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); - return report; + return; case 1: /* 2 port */ val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070; break; @@ -3040,27 +2994,39 @@ static int rt5645_irq_detection(struct rt5645_priv *rt5645) snd_soc_jack_report(rt5645->btn_jack, report, SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); +} + +static irqreturn_t rt5645_irq(int irq, void *data) +{ + struct rt5645_priv *rt5645 = data; + + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(250)); - return report; + return IRQ_HANDLED; } static int rt5645_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); rt5645->codec = codec; switch (rt5645->codec_type) { case CODEC_TYPE_RT5645: - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_new_controls(dapm, + rt5645_specific_dapm_widgets, + ARRAY_SIZE(rt5645_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, rt5645_specific_dapm_routes, ARRAY_SIZE(rt5645_specific_dapm_routes)); break; case CODEC_TYPE_RT5650: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5650_specific_dapm_widgets, ARRAY_SIZE(rt5650_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5650_specific_dapm_routes, ARRAY_SIZE(rt5650_specific_dapm_routes)); break; @@ -3070,9 +3036,9 @@ static int rt5645_probe(struct snd_soc_codec *codec) /* for JD function */ if (rt5645->pdata.jd_mode) { - snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "JD Power"); + snd_soc_dapm_force_enable_pin(dapm, "LDO2"); + snd_soc_dapm_sync(dapm); } return 0; @@ -3113,7 +3079,7 @@ static int rt5645_resume(struct snd_soc_codec *codec) #define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) -static struct snd_soc_dai_ops rt5645_aif_dai_ops = { +static const struct snd_soc_dai_ops rt5645_aif_dai_ops = { .hw_params = rt5645_hw_params, .set_fmt = rt5645_set_dai_fmt, .set_sysclk = rt5645_set_dai_sysclk, @@ -3224,7 +3190,7 @@ static int strago_quirk_cb(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id dmi_platform_intel_braswell[] = { +static const struct dmi_system_id dmi_platform_intel_braswell[] = { { .ident = "Intel Strago", .callback = strago_quirk_cb, @@ -3232,6 +3198,13 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Strago"), }, }, + { + .ident = "Google Celes", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Celes"), + }, + }, { } }; @@ -3254,7 +3227,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, { struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5645_priv *rt5645; - int ret; + int ret, i; unsigned int val; rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), @@ -3288,6 +3261,24 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, return ret; } + for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) + rt5645->supplies[i].supply = rt5645_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(rt5645->supplies), + rt5645->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5645->supplies), + rt5645->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val); switch (val) { @@ -3299,16 +3290,10 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, break; default: dev_err(&i2c->dev, - "Device with ID register %x is not rt5645 or rt5650\n", + "Device with ID register %#x is not rt5645 or rt5650\n", val); - return -ENODEV; - } - - if (rt5645->codec_type == CODEC_TYPE_RT5650) { - ret = rt5650_calibration(rt5645); - - if (ret < 0) - pr_err("calibration failed!\n"); + ret = -ENODEV; + goto err_enable; } regmap_write(rt5645->regmap, RT5645_RESET, 0); @@ -3398,8 +3383,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, RT5645_IRQ_CLK_GATE_CTRL, RT5645_IRQ_CLK_GATE_CTRL); - regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, - RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT); regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, @@ -3439,12 +3422,25 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5645", rt5645); - if (ret) + if (ret) { dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + goto err_enable; + } } - return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, - rt5645_dai, ARRAY_SIZE(rt5645_dai)); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, + rt5645_dai, ARRAY_SIZE(rt5645_dai)); + if (ret) + goto err_irq; + + return 0; + +err_irq: + if (rt5645->i2c->irq) + free_irq(rt5645->i2c->irq, rt5645); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); + return ret; } static int rt5645_i2c_remove(struct i2c_client *i2c) @@ -3457,18 +3453,31 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) cancel_delayed_work_sync(&rt5645->jack_detect_work); snd_soc_unregister_codec(&i2c->dev); + regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); return 0; } +static void rt5645_i2c_shutdown(struct i2c_client *i2c) +{ + struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c); + + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, + RT5645_RING2_SLEEVE_GND, RT5645_RING2_SLEEVE_GND); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD, + RT5645_CBJ_MN_JD); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN, + 0); +} + static struct i2c_driver rt5645_i2c_driver = { .driver = { .name = "rt5645", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(rt5645_acpi_match), }, .probe = rt5645_i2c_probe, - .remove = rt5645_i2c_remove, + .remove = rt5645_i2c_remove, + .shutdown = rt5645_i2c_shutdown, .id_table = rt5645_i2c_id, }; module_i2c_driver(rt5645_i2c_driver); diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 278bb9f..0e4cfc6 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2115,6 +2115,7 @@ enum { #define RT5645_JD_PSV_MODE (0x1 << 12) #define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11) #define RT5645_MICINDET_MANU (0x1 << 7) +#define RT5645_RING2_SLEEVE_GND (0x1 << 5) /* Vendor ID (0xfd) */ #define RT5645_VER_C 0x2 @@ -2181,32 +2182,6 @@ enum { int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec, unsigned int filter_mask, unsigned int clk_src); -struct rt5645_priv { - struct snd_soc_codec *codec; - struct rt5645_platform_data pdata; - struct regmap *regmap; - struct i2c_client *i2c; - struct gpio_desc *gpiod_hp_det; - struct snd_soc_jack *hp_jack; - struct snd_soc_jack *mic_jack; - struct snd_soc_jack *btn_jack; - struct delayed_work jack_detect_work; - - int codec_type; - int sysclk; - int sysclk_src; - int lrck[RT5645_AIFS]; - int bclk[RT5645_AIFS]; - int master[RT5645_AIFS]; - - int pll_src; - int pll_in; - int pll_out; - - int jack_type; - bool en_button_func; -}; - int rt5645_set_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, struct snd_soc_jack *btn_jack); diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index a3506e1..1d40318 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -46,7 +46,7 @@ static const struct regmap_range_cfg rt5651_ranges[] = { .window_len = 0x1, }, }; -static struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5651_PR_BASE + 0x3d, 0x3e00}, }; @@ -292,16 +292,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ -static unsigned int bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const DECLARE_TLV_DB_RANGE(bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), - 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); /* Interface data select */ static const char * const rt5651_data_select[] = { @@ -378,10 +377,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); - int idx = -EINVAL; - - idx = rl6231_calc_dmic_clk(rt5651->sysclk); + int idx, rate; + rate = rt5651->sysclk / rl6231_get_pre_div(rt5651->regmap, + RT5651_ADDA_CLK1, RT5651_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); if (idx < 0) dev_err(codec->dev, "Failed to set DMIC clock\n"); else @@ -1769,7 +1769,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret); if (ret != RT5651_DEVICE_ID_VALUE) { dev_err(&i2c->dev, - "Device with ID register %x is not rt5651\n", ret); + "Device with ID register %#x is not rt5651\n", ret); return -ENODEV; } @@ -1806,7 +1806,6 @@ static int rt5651_i2c_remove(struct i2c_client *i2c) static struct i2c_driver rt5651_i2c_driver = { .driver = { .name = "rt5651", - .owner = THIS_MODULE, }, .probe = rt5651_i2c_probe, .remove = rt5651_i2c_remove, diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index a9123d4..49a9e70 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5670_ranges[] = { .window_len = 0x1, }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { { RT5670_PR_BASE + 0x14, 0x9a8a }, { RT5670_PR_BASE + 0x38, 0x3ba1 }, { RT5670_PR_BASE + 0x3d, 0x3640 }, @@ -592,16 +592,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ -static unsigned int bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const DECLARE_TLV_DB_RANGE(bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), - 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); /* Interface data select */ static const char * const rt5670_data_select[] = { @@ -683,10 +682,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); - int idx = -EINVAL; - - idx = rl6231_calc_dmic_clk(rt5670->sysclk); + int idx, rate; + rate = rt5670->sysclk / rl6231_get_pre_div(rt5670->regmap, + RT5670_ADDA_CLK1, RT5670_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); if (idx < 0) dev_err(codec->dev, "Failed to set DMIC clock\n"); else @@ -2720,7 +2720,7 @@ static int rt5670_resume(struct snd_soc_codec *codec) #define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) -static struct snd_soc_dai_ops rt5670_aif_dai_ops = { +static const struct snd_soc_dai_ops rt5670_aif_dai_ops = { .hw_params = rt5670_hw_params, .set_fmt = rt5670_set_dai_fmt, .set_sysclk = rt5670_set_dai_sysclk, @@ -2863,7 +2863,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, regmap_read(rt5670->regmap, RT5670_VENDOR_ID2, &val); if (val != RT5670_DEVICE_ID) { dev_err(&i2c->dev, - "Device with ID register %x is not rt5670/72\n", val); + "Device with ID register %#x is not rt5670/72\n", val); return -ENODEV; } @@ -3043,7 +3043,6 @@ static int rt5670_i2c_remove(struct i2c_client *i2c) static struct i2c_driver rt5670_i2c_driver = { .driver = { .name = "rt5670", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(rt5670_acpi_match), }, .probe = rt5670_i2c_probe, diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index ef6348c..3505aaf 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -31,84 +31,197 @@ #include "rt5677-spi.h" +#define RT5677_SPI_BURST_LEN 240 +#define RT5677_SPI_HEADER 5 +#define RT5677_SPI_FREQ 6000000 + +/* The AddressPhase and DataPhase of SPI commands are MSB first on the wire. + * DataPhase word size of 16-bit commands is 2 bytes. + * DataPhase word size of 32-bit commands is 4 bytes. + * DataPhase word size of burst commands is 8 bytes. + * The DSP CPU is little-endian. + */ +#define RT5677_SPI_WRITE_BURST 0x5 +#define RT5677_SPI_READ_BURST 0x4 +#define RT5677_SPI_WRITE_32 0x3 +#define RT5677_SPI_READ_32 0x2 +#define RT5677_SPI_WRITE_16 0x1 +#define RT5677_SPI_READ_16 0x0 + static struct spi_device *g_spi; +static DEFINE_MUTEX(spi_mutex); -/** - * rt5677_spi_write - Write data to SPI. - * @txbuf: Data Buffer for writing. - * @len: Data length. +/* Select a suitable transfer command for the next transfer to ensure + * the transfer address is always naturally aligned while minimizing + * the total number of transfers required. + * + * 3 transfer commands are available: + * RT5677_SPI_READ/WRITE_16: Transfer 2 bytes + * RT5677_SPI_READ/WRITE_32: Transfer 4 bytes + * RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes + * + * For example, reading 260 bytes at 0x60030002 uses the following commands: + * 0x60030002 RT5677_SPI_READ_16 2 bytes + * 0x60030004 RT5677_SPI_READ_32 4 bytes + * 0x60030008 RT5677_SPI_READ_BURST 240 bytes + * 0x600300F8 RT5677_SPI_READ_BURST 8 bytes + * 0x60030100 RT5677_SPI_READ_32 4 bytes + * 0x60030104 RT5677_SPI_READ_16 2 bytes * + * Input: + * @read: true for read commands; false for write commands + * @align: alignment of the next transfer address + * @remain: number of bytes remaining to transfer * - * Returns true for success. + * Output: + * @len: number of bytes to transfer with the selected command + * Returns the selected command */ -int rt5677_spi_write(u8 *txbuf, size_t len) +static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len) { - int status; - - status = spi_write(g_spi, txbuf, len); - - if (status) - dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status); - - return status; + u8 cmd; + + if (align == 2 || align == 6 || remain == 2) { + cmd = RT5677_SPI_READ_16; + *len = 2; + } else if (align == 4 || remain <= 6) { + cmd = RT5677_SPI_READ_32; + *len = 4; + } else { + cmd = RT5677_SPI_READ_BURST; + *len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN); + } + return read ? cmd : cmd + 1; } -EXPORT_SYMBOL_GPL(rt5677_spi_write); -/** - * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address. - * @addr: Start address. - * @txbuf: Data Buffer for writng. - * @len: Data length, it must be a multiple of 8. - * - * - * Returns true for success. +/* Copy dstlen bytes from src to dst, while reversing byte order for each word. + * If srclen < dstlen, zeros are padded. */ -int rt5677_spi_burst_write(u32 addr, const struct firmware *fw) +static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen) { - u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE; - u8 *write_buf; - unsigned int i, end, offset = 0; - - write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL); - - if (write_buf == NULL) - return -ENOMEM; - - while (offset < fw->size) { - if (offset + RT5677_SPI_BUF_LEN <= fw->size) - end = RT5677_SPI_BUF_LEN; - else - end = fw->size % RT5677_SPI_BUF_LEN; - - write_buf[0] = spi_cmd; - write_buf[1] = ((addr + offset) & 0xff000000) >> 24; - write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; - write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; - write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; - - for (i = 0; i < end; i += 8) { - write_buf[i + 12] = fw->data[offset + i + 0]; - write_buf[i + 11] = fw->data[offset + i + 1]; - write_buf[i + 10] = fw->data[offset + i + 2]; - write_buf[i + 9] = fw->data[offset + i + 3]; - write_buf[i + 8] = fw->data[offset + i + 4]; - write_buf[i + 7] = fw->data[offset + i + 5]; - write_buf[i + 6] = fw->data[offset + i + 6]; - write_buf[i + 5] = fw->data[offset + i + 7]; + u32 w, i, si; + u32 word_size = min_t(u32, dstlen, 8); + + for (w = 0; w < dstlen; w += word_size) { + for (i = 0; i < word_size; i++) { + si = w + word_size - i - 1; + dst[w + i] = si < srclen ? src[si] : 0; } + } +} - write_buf[end + 5] = spi_cmd; +/* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */ +int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) +{ + u32 offset; + int status = 0; + struct spi_transfer t[2]; + struct spi_message m; + /* +4 bytes is for the DummyPhase following the AddressPhase */ + u8 header[RT5677_SPI_HEADER + 4]; + u8 body[RT5677_SPI_BURST_LEN]; + u8 spi_cmd; + u8 *cb = rxbuf; + + if (!g_spi) + return -ENODEV; + + if ((addr & 1) || (len & 1)) { + dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len); + return -EACCES; + } - rt5677_spi_write(write_buf, end + 6); + memset(t, 0, sizeof(t)); + t[0].tx_buf = header; + t[0].len = sizeof(header); + t[0].speed_hz = RT5677_SPI_FREQ; + t[1].rx_buf = body; + t[1].speed_hz = RT5677_SPI_FREQ; + spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); + + for (offset = 0; offset < len; offset += t[1].len) { + spi_cmd = rt5677_spi_select_cmd(true, (addr + offset) & 7, + len - offset, &t[1].len); + + /* Construct SPI message header */ + header[0] = spi_cmd; + header[1] = ((addr + offset) & 0xff000000) >> 24; + header[2] = ((addr + offset) & 0x00ff0000) >> 16; + header[3] = ((addr + offset) & 0x0000ff00) >> 8; + header[4] = ((addr + offset) & 0x000000ff) >> 0; + + mutex_lock(&spi_mutex); + status |= spi_sync(g_spi, &m); + mutex_unlock(&spi_mutex); + + /* Copy data back to caller buffer */ + rt5677_spi_reverse(cb + offset, t[1].len, body, t[1].len); + } + return status; +} +EXPORT_SYMBOL_GPL(rt5677_spi_read); - offset += RT5677_SPI_BUF_LEN; +/* Write DSP address space using SPI. addr has to be 2-byte aligned. + * If len is not 2-byte aligned, an extra byte of zero is written at the end + * as padding. + */ +int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) +{ + u32 offset, len_with_pad = len; + int status = 0; + struct spi_transfer t; + struct spi_message m; + /* +1 byte is for the DummyPhase following the DataPhase */ + u8 buf[RT5677_SPI_HEADER + RT5677_SPI_BURST_LEN + 1]; + u8 *body = buf + RT5677_SPI_HEADER; + u8 spi_cmd; + const u8 *cb = txbuf; + + if (!g_spi) + return -ENODEV; + + if (addr & 1) { + dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len); + return -EACCES; } - kfree(write_buf); + if (len & 1) + len_with_pad = len + 1; + + memset(&t, 0, sizeof(t)); + t.tx_buf = buf; + t.speed_hz = RT5677_SPI_FREQ; + spi_message_init_with_transfers(&m, &t, 1); + + for (offset = 0; offset < len_with_pad;) { + spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7, + len_with_pad - offset, &t.len); + + /* Construct SPI message header */ + buf[0] = spi_cmd; + buf[1] = ((addr + offset) & 0xff000000) >> 24; + buf[2] = ((addr + offset) & 0x00ff0000) >> 16; + buf[3] = ((addr + offset) & 0x0000ff00) >> 8; + buf[4] = ((addr + offset) & 0x000000ff) >> 0; + + /* Fetch data from caller buffer */ + rt5677_spi_reverse(body, t.len, cb + offset, len - offset); + offset += t.len; + t.len += RT5677_SPI_HEADER + 1; + + mutex_lock(&spi_mutex); + status |= spi_sync(g_spi, &m); + mutex_unlock(&spi_mutex); + } + return status; +} +EXPORT_SYMBOL_GPL(rt5677_spi_write); - return 0; +int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw) +{ + return rt5677_spi_write(addr, fw->data, fw->size); } -EXPORT_SYMBOL_GPL(rt5677_spi_burst_write); +EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware); static int rt5677_spi_probe(struct spi_device *spi) { diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h index ec41b2b..662db16 100644 --- a/sound/soc/codecs/rt5677-spi.h +++ b/sound/soc/codecs/rt5677-spi.h @@ -12,10 +12,8 @@ #ifndef __RT5677_SPI_H__ #define __RT5677_SPI_H__ -#define RT5677_SPI_BUF_LEN 240 -#define RT5677_SPI_CMD_BURST_WRITE 0x05 - -int rt5677_spi_write(u8 *txbuf, size_t len); -int rt5677_spi_burst_write(u32 addr, const struct firmware *fw); +int rt5677_spi_read(u32 addr, void *rxbuf, size_t len); +int rt5677_spi_write(u32 addr, const void *txbuf, size_t len); +int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw); #endif /* __RT5677_SPI_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 31d969a..b4cd7e3 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -15,13 +15,12 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/firmware.h> -#include <linux/gpio.h> +#include <linux/property.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -54,7 +53,7 @@ static const struct regmap_range_cfg rt5677_ranges[] = { }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5677_ASRC_12, 0x0018}, {RT5677_PR_BASE + 0x3d, 0x364d}, {RT5677_PR_BASE + 0x17, 0x4fc0}, @@ -746,14 +745,14 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1, codec->dev); if (ret == 0) { - rt5677_spi_burst_write(0x50000000, rt5677->fw1); + rt5677_spi_write_firmware(0x50000000, rt5677->fw1); release_firmware(rt5677->fw1); } ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2, codec->dev); if (ret == 0) { - rt5677_spi_burst_write(0x60000000, rt5677->fw2); + rt5677_spi_write_firmware(0x60000000, rt5677->fw2); release_firmware(rt5677->fw2); } @@ -789,16 +788,15 @@ static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ -static unsigned int bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const DECLARE_TLV_DB_RANGE(bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), - 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -917,8 +915,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); - int idx = rl6231_calc_dmic_clk(rt5677->lrck[RT5677_AIF1] << 8); + int idx, rate; + rate = rt5677->sysclk / rl6231_get_pre_div(rt5677->regmap, + RT5677_CLK_TREE_CTRL1, RT5677_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); if (idx < 0) dev_err(codec->dev, "Failed to set DMIC clock\n"); else @@ -4764,10 +4765,8 @@ static int rt5677_remove(struct snd_soc_codec *codec) struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); - if (gpio_is_valid(rt5677->pow_ldo2)) - gpio_set_value_cansleep(rt5677->pow_ldo2, 0); - if (gpio_is_valid(rt5677->reset_pin)) - gpio_set_value_cansleep(rt5677->reset_pin, 0); + gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); + gpiod_set_value_cansleep(rt5677->reset_pin, 0); return 0; } @@ -4781,10 +4780,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec) regcache_cache_only(rt5677->regmap, true); regcache_mark_dirty(rt5677->regmap); - if (gpio_is_valid(rt5677->pow_ldo2)) - gpio_set_value_cansleep(rt5677->pow_ldo2, 0); - if (gpio_is_valid(rt5677->reset_pin)) - gpio_set_value_cansleep(rt5677->reset_pin, 0); + gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); + gpiod_set_value_cansleep(rt5677->reset_pin, 0); } return 0; @@ -4795,12 +4792,9 @@ static int rt5677_resume(struct snd_soc_codec *codec) struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); if (!rt5677->dsp_vad_en) { - if (gpio_is_valid(rt5677->pow_ldo2)) - gpio_set_value_cansleep(rt5677->pow_ldo2, 1); - if (gpio_is_valid(rt5677->reset_pin)) - gpio_set_value_cansleep(rt5677->reset_pin, 1); - if (gpio_is_valid(rt5677->pow_ldo2) || - gpio_is_valid(rt5677->reset_pin)) + gpiod_set_value_cansleep(rt5677->pow_ldo2, 1); + gpiod_set_value_cansleep(rt5677->reset_pin, 1); + if (rt5677->pow_ldo2 || rt5677->reset_pin) msleep(10); regcache_cache_only(rt5677->regmap, false); @@ -4863,7 +4857,7 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) #define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) -static struct snd_soc_dai_ops rt5677_aif_dai_ops = { +static const struct snd_soc_dai_ops rt5677_aif_dai_ops = { .hw_params = rt5677_hw_params, .set_fmt = rt5677_set_dai_fmt, .set_sysclk = rt5677_set_dai_sysclk, @@ -5024,45 +5018,29 @@ static const struct i2c_device_id rt5677_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); -static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) +static void rt5677_read_device_properties(struct rt5677_priv *rt5677, + struct device *dev) { - rt5677->pdata.in1_diff = of_property_read_bool(np, - "realtek,in1-differential"); - rt5677->pdata.in2_diff = of_property_read_bool(np, - "realtek,in2-differential"); - rt5677->pdata.lout1_diff = of_property_read_bool(np, - "realtek,lout1-differential"); - rt5677->pdata.lout2_diff = of_property_read_bool(np, - "realtek,lout2-differential"); - rt5677->pdata.lout3_diff = of_property_read_bool(np, - "realtek,lout3-differential"); - - rt5677->pow_ldo2 = of_get_named_gpio(np, - "realtek,pow-ldo2-gpio", 0); - rt5677->reset_pin = of_get_named_gpio(np, - "realtek,reset-gpio", 0); - - /* - * POW_LDO2 is optional (it may be statically tied on the board). - * -ENOENT means that the property doesn't exist, i.e. there is no - * GPIO, so is not an error. Any other error code means the property - * exists, but could not be parsed. - */ - if (!gpio_is_valid(rt5677->pow_ldo2) && - (rt5677->pow_ldo2 != -ENOENT)) - return rt5677->pow_ldo2; - if (!gpio_is_valid(rt5677->reset_pin) && - (rt5677->reset_pin != -ENOENT)) - return rt5677->reset_pin; - - of_property_read_u8_array(np, "realtek,gpio-config", - rt5677->pdata.gpio_config, RT5677_GPIO_NUM); - - of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio); - of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio); - of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio); - - return 0; + rt5677->pdata.in1_diff = device_property_read_bool(dev, + "realtek,in1-differential"); + rt5677->pdata.in2_diff = device_property_read_bool(dev, + "realtek,in2-differential"); + rt5677->pdata.lout1_diff = device_property_read_bool(dev, + "realtek,lout1-differential"); + rt5677->pdata.lout2_diff = device_property_read_bool(dev, + "realtek,lout2-differential"); + rt5677->pdata.lout3_diff = device_property_read_bool(dev, + "realtek,lout3-differential"); + + device_property_read_u8_array(dev, "realtek,gpio-config", + rt5677->pdata.gpio_config, RT5677_GPIO_NUM); + + device_property_read_u32(dev, "realtek,jd1-gpio", + &rt5677->pdata.jd1_gpio); + device_property_read_u32(dev, "realtek,jd2-gpio", + &rt5677->pdata.jd2_gpio); + device_property_read_u32(dev, "realtek,jd3-gpio", + &rt5677->pdata.jd3_gpio); } static struct regmap_irq rt5677_irqs[] = { @@ -5145,43 +5123,29 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, if (pdata) rt5677->pdata = *pdata; + else + rt5677_read_device_properties(rt5677, &i2c->dev); - if (i2c->dev.of_node) { - ret = rt5677_parse_dt(rt5677, i2c->dev.of_node); - if (ret) { - dev_err(&i2c->dev, "Failed to parse device tree: %d\n", - ret); - return ret; - } - } else { - rt5677->pow_ldo2 = -EINVAL; - rt5677->reset_pin = -EINVAL; - } - - if (gpio_is_valid(rt5677->pow_ldo2)) { - ret = devm_gpio_request_one(&i2c->dev, rt5677->pow_ldo2, - GPIOF_OUT_INIT_HIGH, - "RT5677 POW_LDO2"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request POW_LDO2 %d: %d\n", - rt5677->pow_ldo2, ret); - return ret; - } + /* pow-ldo2 and reset are optional. The codec pins may be statically + * connected on the board without gpios. If the gpio device property + * isn't specified, devm_gpiod_get_optional returns NULL. + */ + rt5677->pow_ldo2 = devm_gpiod_get_optional(&i2c->dev, + "realtek,pow-ldo2", GPIOD_OUT_HIGH); + if (IS_ERR(rt5677->pow_ldo2)) { + ret = PTR_ERR(rt5677->pow_ldo2); + dev_err(&i2c->dev, "Failed to request POW_LDO2: %d\n", ret); + return ret; } - - if (gpio_is_valid(rt5677->reset_pin)) { - ret = devm_gpio_request_one(&i2c->dev, rt5677->reset_pin, - GPIOF_OUT_INIT_HIGH, - "RT5677 RESET"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request RESET %d: %d\n", - rt5677->reset_pin, ret); - return ret; - } + rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev, + "realtek,reset", GPIOD_OUT_HIGH); + if (IS_ERR(rt5677->reset_pin)) { + ret = PTR_ERR(rt5677->reset_pin); + dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret); + return ret; } - if (gpio_is_valid(rt5677->pow_ldo2) || - gpio_is_valid(rt5677->reset_pin)) { + if (rt5677->pow_ldo2 || rt5677->reset_pin) { /* Wait a while until I2C bus becomes available. The datasheet * does not specify the exact we should wait but startup * sequence mentiones at least a few milliseconds. @@ -5209,7 +5173,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val); if (val != RT5677_DEVICE_ID) { dev_err(&i2c->dev, - "Device with ID register %x is not rt5677\n", val); + "Device with ID register %#x is not rt5677\n", val); return -ENODEV; } @@ -5273,7 +5237,6 @@ static int rt5677_i2c_remove(struct i2c_client *i2c) static struct i2c_driver rt5677_i2c_driver = { .driver = { .name = "rt5677", - .owner = THIS_MODULE, }, .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 7eca38a..d46855a 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -14,6 +14,7 @@ #include <sound/rt5677.h> #include <linux/gpio/driver.h> +#include <linux/gpio/consumer.h> /* Info */ #define RT5677_RESET 0x00 @@ -1775,8 +1776,8 @@ struct rt5677_priv { int pll_src; int pll_in; int pll_out; - int pow_ldo2; /* POW_LDO2 pin */ - int reset_pin; /* RESET pin */ + struct gpio_desc *pow_ldo2; /* POW_LDO2 pin */ + struct gpio_desc *reset_pin; /* RESET pin */ enum rt5677_type type; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index e673f6c..bfda25e 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -406,11 +406,10 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0); /* tlv for mic gain, 0db 20db 30db 40db */ -static const unsigned int mic_gain_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(mic_gain_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), - 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0), -}; + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0) +); /* tlv for hp volume, -51.5db to 12.0db, step .5db */ static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); @@ -1601,7 +1600,6 @@ MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids); static struct i2c_driver sgtl5000_i2c_driver = { .driver = { .name = "sgtl5000", - .owner = THIS_MODULE, .of_match_table = sgtl5000_dt_ids, }, .probe = sgtl5000_i2c_probe, diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 3e72964..a8402d0 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -208,7 +208,7 @@ out: return err; } -static struct snd_soc_dai_ops si476x_dai_ops = { +static const struct snd_soc_dai_ops si476x_dai_ops = { .hw_params = si476x_codec_hw_params, .set_fmt = si476x_codec_set_dai_fmt, }; diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c index 29cb442..6bfd25c 100644 --- a/sound/soc/codecs/sirf-audio-codec.c +++ b/sound/soc/codecs/sirf-audio-codec.c @@ -370,11 +370,11 @@ static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream, return 0; } -struct snd_soc_dai_ops sirf_audio_codec_dai_ops = { +static const struct snd_soc_dai_ops sirf_audio_codec_dai_ops = { .trigger = sirf_audio_codec_trigger, }; -struct snd_soc_dai_driver sirf_audio_codec_dai = { +static struct snd_soc_dai_driver sirf_audio_codec_dai = { .name = "sirf-audio-codec", .playback = { .stream_name = "Playback", diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index f30de76..ddb0203 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -806,6 +806,14 @@ static int ssm2518_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ssm2518_dt_ids[] = { + { .compatible = "adi,ssm2518", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2518_dt_ids); +#endif + static const struct i2c_device_id ssm2518_i2c_ids[] = { { "ssm2518", 0 }, { } @@ -815,7 +823,7 @@ MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); static struct i2c_driver ssm2518_driver = { .driver = { .name = "ssm2518", - .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ssm2518_dt_ids), }, .probe = ssm2518_i2c_probe, .remove = ssm2518_i2c_remove, diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index 0d9779d..173ba85 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -52,7 +52,6 @@ MODULE_DEVICE_TABLE(of, ssm2602_of_match); static struct i2c_driver ssm2602_i2c_driver = { .driver = { .name = "ssm2602", - .owner = THIS_MODULE, .of_match_table = ssm2602_of_match, }, .probe = ssm2602_i2c_probe, diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 69a773a..4452fea 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -75,11 +75,10 @@ static const struct soc_enum ssm2602_enum[] = { ssm2602_deemph), }; -static const unsigned int ssm260x_outmix_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(ssm260x_outmix_tlv, 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), - 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0), -}; + 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0) +); static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 84a4f5a..e619d56 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -10,6 +10,7 @@ * Licensed under the GPL-2. */ +#include <linux/acpi.h> #include <linux/module.h> #include <linux/init.h> #include <linux/i2c.h> @@ -173,6 +174,12 @@ static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = { SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1, &ssm4567_amplifier_boost_control), + SND_SOC_DAPM_SIGGEN("Sense"), + + SND_SOC_DAPM_PGA("Current Sense", SSM4567_REG_POWER_CTRL, 4, 1, NULL, 0), + SND_SOC_DAPM_PGA("Voltage Sense", SSM4567_REG_POWER_CTRL, 5, 1, NULL, 0), + SND_SOC_DAPM_PGA("VBAT Sense", SSM4567_REG_POWER_CTRL, 6, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("OUT"), }; @@ -180,6 +187,13 @@ static const struct snd_soc_dapm_route ssm4567_routes[] = { { "OUT", NULL, "Amplifier Boost" }, { "Amplifier Boost", "Switch", "DAC" }, { "OUT", NULL, "DAC" }, + + { "Current Sense", NULL, "Sense" }, + { "Voltage Sense", NULL, "Sense" }, + { "VBAT Sense", NULL, "Sense" }, + { "Capture Sense", NULL, "Current Sense" }, + { "Capture Sense", NULL, "Voltage Sense" }, + { "Capture Sense", NULL, "VBAT Sense" }, }; static int ssm4567_hw_params(struct snd_pcm_substream *substream, @@ -387,6 +401,14 @@ static struct snd_soc_dai_driver ssm4567_dai = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32, }, + .capture = { + .stream_name = "Capture Sense", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32, + }, .ops = &ssm4567_dai_ops, }; @@ -456,10 +478,20 @@ static const struct i2c_device_id ssm4567_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids); +#ifdef CONFIG_ACPI + +static const struct acpi_device_id ssm4567_acpi_match[] = { + { "INT343B", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match); + +#endif + static struct i2c_driver ssm4567_driver = { .driver = { .name = "ssm4567", - .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(ssm4567_acpi_match), }, .probe = ssm4567_i2c_probe, .remove = ssm4567_i2c_remove, diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 60eff36..a9844b2 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1144,7 +1144,6 @@ MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id); static struct i2c_driver sta32x_i2c_driver = { .driver = { .name = "sta32x", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(st32x_dt_ids), }, .probe = sta32x_i2c_probe, diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index bd819a3..33a4612 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1264,7 +1264,6 @@ 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, diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 4f70378..2cdaca9 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -339,9 +339,6 @@ static int sta529_i2c_probe(struct i2c_client *i2c, struct sta529 *sta529; int ret; - if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EINVAL; - sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL); if (!sta529) return -ENOMEM; @@ -379,7 +376,6 @@ MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); static struct i2c_driver sta529_i2c_driver = { .driver = { .name = "sta529", - .owner = THIS_MODULE, }, .probe = sta529_i2c_probe, .remove = sta529_i2c_remove, diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index ed4cca7..0945c51 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -28,6 +28,9 @@ #include "stac9766.h" +#define STAC9766_VENDOR_ID 0x83847666 +#define STAC9766_VENDOR_ID_MASK 0xffffffff + /* * STAC9766 register cache */ @@ -239,45 +242,12 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int stac9766_reset(struct snd_soc_codec *codec, int try_warm) -{ - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - - if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(ac97); - if (stac9766_ac97_read(codec, 0) == stac9766_reg[0]) - return 1; - } - - soc_ac97_ops->reset(ac97); - if (soc_ac97_ops->warm_reset) - soc_ac97_ops->warm_reset(ac97); - if (stac9766_ac97_read(codec, 0) != stac9766_reg[0]) - return -EIO; - return 0; -} - static int stac9766_codec_resume(struct snd_soc_codec *codec) { struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 id, reset; - reset = 0; - /* give the codec an AC97 warm reset to start the link */ -reset: - if (reset > 5) { - dev_err(codec->dev, "Failed to resume\n"); - return -EIO; - } - ac97->bus->ops->warm_reset(ac97); - id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2); - if (id != 0x4c13) { - stac9766_reset(codec, 0); - reset++; - goto reset; - } - - return 0; + return snd_ac97_reset(ac97, true, STAC9766_VENDOR_ID, + STAC9766_VENDOR_ID_MASK); } static const struct snd_soc_dai_ops stac9766_dai_ops_analog = { @@ -330,28 +300,15 @@ static struct snd_soc_dai_driver stac9766_dai[] = { static int stac9766_codec_probe(struct snd_soc_codec *codec) { struct snd_ac97 *ac97; - int ret = 0; - ac97 = snd_soc_new_ac97_codec(codec); + ac97 = snd_soc_new_ac97_codec(codec, STAC9766_VENDOR_ID, + STAC9766_VENDOR_ID_MASK); if (IS_ERR(ac97)) return PTR_ERR(ac97); snd_soc_codec_set_drvdata(codec, ac97); - /* do a cold reset for the controller and then try - * a warm reset followed by an optional cold reset for codec */ - stac9766_reset(codec, 0); - ret = stac9766_reset(codec, 1); - if (ret < 0) { - dev_err(codec->dev, "Failed to reset: AC97 link error\n"); - goto codec_err; - } - return 0; - -codec_err: - snd_soc_free_ac97_codec(ac97); - return ret; } static int stac9766_codec_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c new file mode 100644 index 0000000..160d61a --- /dev/null +++ b/sound/soc/codecs/sti-sas.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/mfd/syscon.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +/* chipID supported */ +#define CHIPID_STIH416 0 +#define CHIPID_STIH407 1 + +/* DAC definitions */ + +/* stih416 DAC registers */ +/* sysconf 2517: Audio-DAC-Control */ +#define STIH416_AUDIO_DAC_CTRL 0x00000814 +/* sysconf 2519: Audio-Gue-Control */ +#define STIH416_AUDIO_GLUE_CTRL 0x0000081C + +#define STIH416_DAC_NOT_STANDBY 0x3 +#define STIH416_DAC_SOFTMUTE 0x4 +#define STIH416_DAC_ANA_NOT_PWR 0x5 +#define STIH416_DAC_NOT_PNDBG 0x6 + +#define STIH416_DAC_NOT_STANDBY_MASK BIT(STIH416_DAC_NOT_STANDBY) +#define STIH416_DAC_SOFTMUTE_MASK BIT(STIH416_DAC_SOFTMUTE) +#define STIH416_DAC_ANA_NOT_PWR_MASK BIT(STIH416_DAC_ANA_NOT_PWR) +#define STIH416_DAC_NOT_PNDBG_MASK BIT(STIH416_DAC_NOT_PNDBG) + +/* stih407 DAC registers */ +/* sysconf 5041: Audio-Gue-Control */ +#define STIH407_AUDIO_GLUE_CTRL 0x000000A4 +/* sysconf 5042: Audio-DAC-Control */ +#define STIH407_AUDIO_DAC_CTRL 0x000000A8 + +/* DAC definitions */ +#define STIH407_DAC_SOFTMUTE 0x0 +#define STIH407_DAC_STANDBY_ANA 0x1 +#define STIH407_DAC_STANDBY 0x2 + +#define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE) +#define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA) +#define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY) + +/* SPDIF definitions */ +#define SPDIF_BIPHASE_ENABLE 0x6 +#define SPDIF_BIPHASE_IDLE 0x7 + +#define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE) +#define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE) + +enum { + STI_SAS_DAI_SPDIF_OUT, + STI_SAS_DAI_ANALOG_OUT, +}; + +static const struct reg_default stih416_sas_reg_defaults[] = { + { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, + { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, +}; + +static const struct reg_default stih407_sas_reg_defaults[] = { + { STIH416_AUDIO_DAC_CTRL, 0x000000000 }, + { STIH416_AUDIO_GLUE_CTRL, 0x00000040 }, +}; + +struct sti_dac_audio { + struct regmap *regmap; + struct regmap *virt_regmap; + struct regmap_field **field; + struct reset_control *rst; + int mclk; +}; + +struct sti_spdif_audio { + struct regmap *regmap; + struct regmap_field **field; + int mclk; +}; + +/* device data structure */ +struct sti_sas_dev_data { + const int chipid; /* IC version */ + const struct regmap_config *regmap; + const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ + const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ + const int num_dapm_widgets; /* dapms declaration */ + const struct snd_soc_dapm_route *dapm_routes; /* route declaration */ + const int num_dapm_routes; /* route declaration */ +}; + +/* driver data structure */ +struct sti_sas_data { + struct device *dev; + const struct sti_sas_dev_data *dev_data; + struct sti_dac_audio dac; + struct sti_spdif_audio spdif; +}; + +/* Read a register from the sysconf reg bank */ +static int sti_sas_read_reg(void *context, unsigned int reg, + unsigned int *value) +{ + struct sti_sas_data *drvdata = context; + int status; + u32 val; + + status = regmap_read(drvdata->dac.regmap, reg, &val); + *value = (unsigned int)val; + + return status; +} + +/* Read a register from the sysconf reg bank */ +static int sti_sas_write_reg(void *context, unsigned int reg, + unsigned int value) +{ + struct sti_sas_data *drvdata = context; + int status; + + status = regmap_write(drvdata->dac.regmap, reg, value); + + return status; +} + +static int sti_sas_init_sas_registers(struct snd_soc_codec *codec, + struct sti_sas_data *data) +{ + int ret; + /* + * DAC and SPDIF are activated by default + * put them in IDLE to save power + */ + + /* Initialise bi-phase formatter to disabled */ + ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_ENABLE_MASK, 0); + + if (!ret) + /* Initialise bi-phase formatter idle value to 0 */ + ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_IDLE_MASK, 0); + if (ret < 0) { + dev_err(codec->dev, "Failed to update SPDIF registers"); + return ret; + } + + /* Init DAC configuration */ + switch (data->dev_data->chipid) { + case CHIPID_STIH407: + /* init configuration */ + ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_MASK, + STIH407_DAC_STANDBY_MASK); + + if (!ret) + ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_ANA_MASK, + STIH407_DAC_STANDBY_ANA_MASK); + if (!ret) + ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + STIH407_DAC_SOFTMUTE_MASK); + break; + case CHIPID_STIH416: + ret = snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_NOT_STANDBY_MASK, 0); + if (!ret) + ret = snd_soc_update_bits(codec, + STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_ANA_NOT_PWR, 0); + if (!ret) + ret = snd_soc_update_bits(codec, + STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_NOT_PNDBG_MASK, + 0); + if (!ret) + ret = snd_soc_update_bits(codec, + STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_SOFTMUTE_MASK, + STIH416_DAC_SOFTMUTE_MASK); + break; + default: + return -EINVAL; + } + + if (ret < 0) { + dev_err(codec->dev, "Failed to update DAC registers"); + return ret; + } + + return ret; +} + +/* + * DAC + */ +static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /* Sanity check only */ + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(dai->codec->dev, + "%s: ERROR: Unsupporter master mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +static int stih416_dac_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + /* Get reset control */ + dac->rst = devm_reset_control_get(codec->dev, "dac_rst"); + if (IS_ERR(dac->rst)) { + dev_err(dai->codec->dev, + "%s: ERROR: DAC reset control not defined !\n", + __func__); + dac->rst = NULL; + return -EFAULT; + } + /* Put the DAC into reset */ + reset_control_assert(dac->rst); + + return 0; +} + +static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = { + SND_SOC_DAPM_PGA("DAC bandgap", STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_NOT_PNDBG_MASK, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_ANA_NOT_PWR, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_NOT_STANDBY, 0), + SND_SOC_DAPM_OUTPUT("DAC Output"), +}; + +static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { + SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_ANA, 1, NULL, 0), + SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY, 1), + SND_SOC_DAPM_OUTPUT("DAC Output"), +}; + +static const struct snd_soc_dapm_route stih416_sas_route[] = { + {"DAC Output", NULL, "DAC bandgap"}, + {"DAC Output", NULL, "DAC standby ana"}, + {"DAC standby ana", NULL, "DAC standby"}, +}; + +static const struct snd_soc_dapm_route stih407_sas_route[] = { + {"DAC Output", NULL, "DAC standby ana"}, + {"DAC standby ana", NULL, "DAC standby"}, +}; + +static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) { + return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_SOFTMUTE_MASK, + STIH416_DAC_SOFTMUTE_MASK); + } else { + return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, + STIH416_DAC_SOFTMUTE_MASK, 0); + } +} + +static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) { + return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + STIH407_DAC_SOFTMUTE_MASK); + } else { + return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + 0); + } +} + +/* + * SPDIF + */ +static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(dai->codec->dev, + "%s: ERROR: Unsupporter master mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +/* + * sti_sas_spdif_trigger: + * Trigger function is used to ensure that BiPhase Formater is disabled + * before CPU dai is stopped. + * This is mandatory to avoid that BPF is stalled + */ +static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_ENABLE_MASK, + SPDIF_BIPHASE_ENABLE_MASK); + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_ENABLE_MASK, + 0); + default: + return -EINVAL; + } +} + +static bool sti_sas_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg == STIH407_AUDIO_GLUE_CTRL) + return true; + + return false; +} + +/* + * CODEC DAIS + */ + +/* + * sti_sas_set_sysclk: + * get MCLK input frequency to check that MCLK-FS ratio is coherent + */ +static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); + + if (dir == SND_SOC_CLOCK_OUT) + return 0; + + if (clk_id != 0) + return -EINVAL; + + switch (dai->id) { + case STI_SAS_DAI_SPDIF_OUT: + drvdata->spdif.mclk = freq; + break; + + case STI_SAS_DAI_ANALOG_OUT: + drvdata->dac.mclk = freq; + break; + } + + return 0; +} + +static int sti_sas_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); + struct snd_pcm_runtime *runtime = substream->runtime; + + switch (dai->id) { + case STI_SAS_DAI_SPDIF_OUT: + if ((drvdata->spdif.mclk / runtime->rate) != 128) { + dev_err(codec->dev, "unexpected mclk-fs ratio"); + return -EINVAL; + } + break; + case STI_SAS_DAI_ANALOG_OUT: + if ((drvdata->dac.mclk / runtime->rate) != 256) { + dev_err(codec->dev, "unexpected mclk-fs ratio"); + return -EINVAL; + } + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops stih416_dac_ops = { + .set_fmt = sti_sas_dac_set_fmt, + .mute_stream = stih416_sas_dac_mute, + .prepare = sti_sas_prepare, + .set_sysclk = sti_sas_set_sysclk, +}; + +static const struct snd_soc_dai_ops stih407_dac_ops = { + .set_fmt = sti_sas_dac_set_fmt, + .mute_stream = stih407_sas_dac_mute, + .prepare = sti_sas_prepare, + .set_sysclk = sti_sas_set_sysclk, +}; + +static const struct regmap_config stih407_sas_regmap = { + .reg_bits = 32, + .val_bits = 32, + + .max_register = STIH407_AUDIO_DAC_CTRL, + .reg_defaults = stih407_sas_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), + .volatile_reg = sti_sas_volatile_register, + .cache_type = REGCACHE_RBTREE, + .reg_read = sti_sas_read_reg, + .reg_write = sti_sas_write_reg, +}; + +static const struct regmap_config stih416_sas_regmap = { + .reg_bits = 32, + .val_bits = 32, + + .max_register = STIH416_AUDIO_DAC_CTRL, + .reg_defaults = stih416_sas_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(stih416_sas_reg_defaults), + .volatile_reg = sti_sas_volatile_register, + .cache_type = REGCACHE_RBTREE, + .reg_read = sti_sas_read_reg, + .reg_write = sti_sas_write_reg, +}; + +static const struct sti_sas_dev_data stih416_data = { + .chipid = CHIPID_STIH416, + .regmap = &stih416_sas_regmap, + .dac_ops = &stih416_dac_ops, + .dapm_widgets = stih416_sas_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets), + .dapm_routes = stih416_sas_route, + .num_dapm_routes = ARRAY_SIZE(stih416_sas_route), +}; + +static const struct sti_sas_dev_data stih407_data = { + .chipid = CHIPID_STIH407, + .regmap = &stih407_sas_regmap, + .dac_ops = &stih407_dac_ops, + .dapm_widgets = stih407_sas_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets), + .dapm_routes = stih407_sas_route, + .num_dapm_routes = ARRAY_SIZE(stih407_sas_route), +}; + +static struct snd_soc_dai_driver sti_sas_dai[] = { + { + .name = "sas-dai-spdif-out", + .id = STI_SAS_DAI_SPDIF_OUT, + .playback = { + .stream_name = "spdif_p", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .set_fmt = sti_sas_spdif_set_fmt, + .trigger = sti_sas_spdif_trigger, + .set_sysclk = sti_sas_set_sysclk, + .prepare = sti_sas_prepare, + } + }, + }, + { + .name = "sas-dai-dac", + .id = STI_SAS_DAI_ANALOG_OUT, + .playback = { + .stream_name = "dac_p", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int sti_sas_resume(struct snd_soc_codec *codec) +{ + struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); + + return sti_sas_init_sas_registers(codec, drvdata); +} +#else +#define sti_sas_resume NULL +#endif + +static int sti_sas_codec_probe(struct snd_soc_codec *codec) +{ + struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); + int ret; + + ret = sti_sas_init_sas_registers(codec, drvdata); + + return ret; +} + +static struct snd_soc_codec_driver sti_sas_driver = { + .probe = sti_sas_codec_probe, + .resume = sti_sas_resume, +}; + +static const struct of_device_id sti_sas_dev_match[] = { + { + .compatible = "st,stih416-sas-codec", + .data = &stih416_data, + }, + { + .compatible = "st,stih407-sas-codec", + .data = &stih407_data, + }, + {}, +}; + +static int sti_sas_driver_probe(struct platform_device *pdev) +{ + struct device_node *pnode = pdev->dev.of_node; + struct sti_sas_data *drvdata; + const struct of_device_id *of_id; + + /* Allocate device structure */ + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + /* Populate data structure depending on compatibility */ + of_id = of_match_node(sti_sas_dev_match, pnode); + if (!of_id->data) { + dev_err(&pdev->dev, "data associated to device is missing"); + return -EINVAL; + } + + drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data; + + /* Initialise device structure */ + drvdata->dev = &pdev->dev; + + /* Request the DAC & SPDIF registers memory region */ + drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata, + drvdata->dev_data->regmap); + if (IS_ERR(drvdata->dac.virt_regmap)) { + dev_err(&pdev->dev, "audio registers not enabled\n"); + return PTR_ERR(drvdata->dac.virt_regmap); + } + + /* Request the syscon region */ + drvdata->dac.regmap = + syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); + if (IS_ERR(drvdata->dac.regmap)) { + dev_err(&pdev->dev, "syscon registers not available\n"); + return PTR_ERR(drvdata->dac.regmap); + } + drvdata->spdif.regmap = drvdata->dac.regmap; + + /* Set DAC dai probe */ + if (drvdata->dev_data->chipid == CHIPID_STIH416) + sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe; + + sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; + + /* Set dapms*/ + sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; + sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; + + sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes; + sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; + + /* Store context */ + dev_set_drvdata(&pdev->dev, drvdata); + + return snd_soc_register_codec(&pdev->dev, &sti_sas_driver, + sti_sas_dai, + ARRAY_SIZE(sti_sas_dai)); +} + +static int sti_sas_driver_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver sti_sas_platform_driver = { + .driver = { + .name = "sti-sas-codec", + .of_match_table = sti_sas_dev_match, + }, + .probe = sti_sas_driver_probe, + .remove = sti_sas_driver_remove, +}; + +module_platform_driver(sti_sas_platform_driver); + +MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms"); +MODULE_AUTHOR("Arnaud.pouliquen@st.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 4f25a7d..e3a0bca 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -38,7 +38,7 @@ #include "tas2552.h" -static struct reg_default tas2552_reg_defs[] = { +static const struct reg_default tas2552_reg_defs[] = { {TAS2552_CFG_1, 0x22}, {TAS2552_CFG_3, 0x80}, {TAS2552_DOUT, 0x00}, @@ -493,8 +493,7 @@ static int tas2552_runtime_suspend(struct device *dev) regcache_cache_only(tas2552->regmap, true); regcache_mark_dirty(tas2552->regmap); - if (tas2552->enable_gpio) - gpiod_set_value(tas2552->enable_gpio, 0); + gpiod_set_value(tas2552->enable_gpio, 0); return 0; } @@ -503,8 +502,7 @@ static int tas2552_runtime_resume(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); - if (tas2552->enable_gpio) - gpiod_set_value(tas2552->enable_gpio, 1); + gpiod_set_value(tas2552->enable_gpio, 1); tas2552_sw_shutdown(tas2552, 0); @@ -520,7 +518,7 @@ static const struct dev_pm_ops tas2552_pm = { NULL) }; -static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { +static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = { .hw_params = tas2552_hw_params, .prepare = tas2552_prepare, .set_sysclk = tas2552_set_dai_sysclk, @@ -585,8 +583,7 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) return ret; } - if (tas2552->enable_gpio) - gpiod_set_value(tas2552->enable_gpio, 1); + gpiod_set_value(tas2552->enable_gpio, 1); ret = pm_runtime_get_sync(codec->dev); if (ret < 0) { @@ -610,8 +607,7 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) return 0; probe_fail: - if (tas2552->enable_gpio) - gpiod_set_value(tas2552->enable_gpio, 0); + gpiod_set_value(tas2552->enable_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), tas2552->supplies); @@ -624,8 +620,7 @@ static int tas2552_codec_remove(struct snd_soc_codec *codec) pm_runtime_put(codec->dev); - if (tas2552->enable_gpio) - gpiod_set_value(tas2552->enable_gpio, 0); + gpiod_set_value(tas2552->enable_gpio, 0); return 0; }; @@ -769,7 +764,6 @@ MODULE_DEVICE_TABLE(of, tas2552_of_match); static struct i2c_driver tas2552_i2c_driver = { .driver = { .name = "tas2552", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tas2552_of_match), .pm = &tas2552_pm, }, diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h index 5746f8f..e34752b 100644 --- a/sound/soc/codecs/tas2552.h +++ b/sound/soc/codecs/tas2552.h @@ -42,7 +42,7 @@ #define TAS2552_BOOST_APT_CTRL 0x14 #define TAS2552_VER_NUM 0x16 #define TAS2552_VBAT_DATA 0x19 -#define TAS2552_MAX_REG 0x20 +#define TAS2552_MAX_REG TAS2552_VBAT_DATA /* CFG1 Register Masks */ #define TAS2552_DEV_RESET (1 << 0) diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 32942be..d49d25d 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -266,10 +266,14 @@ static int tas5086_set_deemph(struct snd_soc_codec *codec) struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); int i, val = 0; - if (priv->deemph) - for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++) - if (tas5086_deemph[i] == priv->rate) + if (priv->deemph) { + for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++) { + if (tas5086_deemph[i] == priv->rate) { val = i; + break; + } + } + } return regmap_update_bits(priv->regmap, TAS5086_SYS_CONTROL_1, TAS5086_DEEMPH_MASK, val); @@ -994,7 +998,6 @@ static int tas5086_i2c_remove(struct i2c_client *i2c) static struct i2c_driver tas5086_i2c_driver = { .driver = { .name = "tas5086", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tas5086_dt_ids), }, .id_table = tas5086_i2c_id, diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 85bcc37..39307ad 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -179,7 +179,7 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { if (!IS_ERR(priv->mclk)) { ret = clk_prepare_enable(priv->mclk); if (ret) { diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index aab0af6..cb5310d 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -160,7 +160,7 @@ static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static struct reg_default tfa9879_regs[] = { +static const struct reg_default tfa9879_regs[] = { { TFA9879_DEVICE_CONTROL, 0x0000 }, /* 0x00 */ { TFA9879_SERIAL_INTERFACE_1, 0x0a18 }, /* 0x01 */ { TFA9879_PCM_IOM2_FORMAT_1, 0x0007 }, /* 0x02 */ @@ -314,7 +314,6 @@ MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); static struct i2c_driver tfa9879_i2c_driver = { .driver = { .name = "tfa9879", - .owner = THIS_MODULE, }, .probe = tfa9879_i2c_probe, .remove = tfa9879_i2c_remove, diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index c4c960f..ee4def4 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1121,7 +1121,7 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = { .num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map), }; -static struct snd_soc_dai_ops aic31xx_dai_ops = { +static const struct snd_soc_dai_ops aic31xx_dai_ops = { .hw_params = aic31xx_hw_params, .set_sysclk = aic31xx_set_dai_sysclk, .set_fmt = aic31xx_set_dai_fmt, @@ -1283,7 +1283,6 @@ MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); static struct i2c_driver aic31xx_i2c_driver = { .driver = { .name = "tlv320aic31xx-codec", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tlv320aic31xx_of_match), }, .probe = aic31xx_i2c_probe, diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index ad6cb90..f2d3191 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -871,7 +871,6 @@ MODULE_DEVICE_TABLE(of, aic32x4_of_id); static struct i2c_driver aic32x4_i2c_driver = { .driver = { .name = "tlv320aic32x4", - .owner = THIS_MODULE, .of_match_table = aic32x4_of_id, }, .probe = aic32x4_i2c_probe, diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index a7cf19b..1a82b19 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1668,7 +1668,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); -static const struct reg_default aic3007_class_d[] = { +static const struct reg_sequence aic3007_class_d[] = { /* Class-D speaker driver init; datasheet p. 46 */ { AIC3X_PAGE_SELECT, 0x0D }, { 0xD, 0x0D }, @@ -1825,7 +1825,6 @@ MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match); static struct i2c_driver aic3x_i2c_driver = { .driver = { .name = "tlv320aic3x-codec", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tlv320aic3x_of_match), }, .probe = aic3x_i2c_probe, diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index d67a311..781398fb 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1585,7 +1585,6 @@ MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id); static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { .name = "tlv320dac33-codec", - .owner = THIS_MODULE, }, .probe = dac33_i2c_probe, .remove = dac33_i2c_remove, diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 6fac9e0..11d85c5 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -259,8 +259,7 @@ static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol, * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going * down in gain. */ -static const unsigned int tpa6130_tlv[] = { - TLV_DB_RANGE_HEAD(10), +static const DECLARE_TLV_DB_RANGE(tpa6130_tlv, 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0), 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0), 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0), @@ -270,8 +269,8 @@ static const unsigned int tpa6130_tlv[] = { 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0), 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0), 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0), - 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0), -}; + 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0) +); static const struct snd_kcontrol_new tpa6130a2_controls[] = { SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume", @@ -280,12 +279,11 @@ static const struct snd_kcontrol_new tpa6130a2_controls[] = { tpa6130_tlv), }; -static const unsigned int tpa6140_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(tpa6140_tlv, 0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0), 9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0), - 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0), -}; + 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0) +); static const struct snd_kcontrol_new tpa6140a2_controls[] = { SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume", @@ -488,7 +486,6 @@ MODULE_DEVICE_TABLE(of, tpa6130a2_of_match); static struct i2c_driver tpa6130a2_i2c_driver = { .driver = { .name = "tpa6130a2", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tpa6130a2_of_match), }, .probe = tpa6130a2_probe, diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 12232d7..4356843 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -23,11 +23,13 @@ #include "ts3a227e.h" struct ts3a227e { + struct device *dev; struct regmap *regmap; struct snd_soc_jack *jack; bool plugged; bool mic_present; unsigned int buttons_held; + int irq; }; /* Button values to be reported on the jack */ @@ -189,16 +191,28 @@ static irqreturn_t ts3a227e_interrupt(int irq, void *data) struct ts3a227e *ts3a227e = (struct ts3a227e *)data; struct regmap *regmap = ts3a227e->regmap; unsigned int int_reg, kp_int_reg, acc_reg, i; + struct device *dev = ts3a227e->dev; + int ret; /* Check for plug/unplug. */ - regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg); + ret = regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg); + if (ret) { + dev_err(dev, "failed to clear interrupt ret=%d\n", ret); + return IRQ_NONE; + } + if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) { regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg); ts3a227e_new_jack_state(ts3a227e, acc_reg); } /* Report any key events. */ - regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg); + ret = regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg); + if (ret) { + dev_err(dev, "failed to clear key interrupt ret=%d\n", ret); + return IRQ_NONE; + } + for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) { if (kp_int_reg & PRESS_MASK(i)) ts3a227e->buttons_held |= (1 << i); @@ -283,6 +297,8 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, ts3a227e); + ts3a227e->dev = dev; + ts3a227e->irq = i2c->irq; ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config); if (IS_ERR(ts3a227e->regmap)) @@ -320,6 +336,32 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, return 0; } +#ifdef CONFIG_PM_SLEEP +static int ts3a227e_suspend(struct device *dev) +{ + struct ts3a227e *ts3a227e = dev_get_drvdata(dev); + + dev_dbg(ts3a227e->dev, "suspend disable irq\n"); + disable_irq(ts3a227e->irq); + + return 0; +} + +static int ts3a227e_resume(struct device *dev) +{ + struct ts3a227e *ts3a227e = dev_get_drvdata(dev); + + dev_dbg(ts3a227e->dev, "resume enable irq\n"); + enable_irq(ts3a227e->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops ts3a227e_pm = { + SET_SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume) +}; + static const struct i2c_device_id ts3a227e_i2c_ids[] = { { "ts3a227e", 0 }, { } @@ -335,7 +377,7 @@ MODULE_DEVICE_TABLE(of, ts3a227e_of_match); static struct i2c_driver ts3a227e_driver = { .driver = { .name = "ts3a227e", - .owner = THIS_MODULE, + .pm = &ts3a227e_pm, .of_match_table = of_match_ptr(ts3a227e_of_match), }, .probe = ts3a227e_i2c_probe, diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 90f5f04..2713e18 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -524,12 +524,11 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); /* Digital bypass gain, mute instead of -30dB */ -static const unsigned int twl4030_dapm_dbypass_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(twl4030_dapm_dbypass_tlv, 0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1), 2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0), - 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), -}; + 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0) +); /* Digital bypass left (TX1L -> RX2L) */ static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control = diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 913edf2..e190263 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -37,74 +37,53 @@ struct uda134x_priv { struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; -}; -/* In-data addresses are hard-coded into the reg-cache values */ -static const char uda134x_reg[UDA134X_REGS_NUM] = { - /* Extended address registers */ - 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - /* Status, data regs */ - 0x00, 0x83, 0x00, 0x40, 0x80, 0xC0, 0x00, + struct regmap *regmap; + struct uda134x_platform_data *pd; }; -/* - * The codec has no support for reading its registers except for peak level... - */ -static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *cache = codec->reg_cache; - - if (reg >= UDA134X_REGS_NUM) - return -1; - return cache[reg]; -} - -/* - * Write the register cache - */ -static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec, - u8 reg, unsigned int value) -{ - u8 *cache = codec->reg_cache; - - if (reg >= UDA134X_REGS_NUM) - return; - cache[reg] = value; -} +static const struct reg_default uda134x_reg_defaults[] = { + { UDA134X_EA000, 0x04 }, + { UDA134X_EA001, 0x04 }, + { UDA134X_EA010, 0x04 }, + { UDA134X_EA011, 0x00 }, + { UDA134X_EA100, 0x00 }, + { UDA134X_EA101, 0x00 }, + { UDA134X_EA110, 0x00 }, + { UDA134X_EA111, 0x00 }, + { UDA134X_STATUS0, 0x00 }, + { UDA134X_STATUS1, 0x03 }, + { UDA134X_DATA000, 0x00 }, + { UDA134X_DATA001, 0x00 }, + { UDA134X_DATA010, 0x00 }, + { UDA134X_DATA011, 0x00 }, + { UDA134X_DATA1, 0x00 }, +}; /* * Write to the uda134x registers * */ -static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg, +static int uda134x_regmap_write(void *context, unsigned int reg, unsigned int value) { + struct uda134x_platform_data *pd = context; int ret; u8 addr; u8 data = value; - struct uda134x_platform_data *pd = codec->control_data; - - pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value); - - if (reg >= UDA134X_REGS_NUM) { - printk(KERN_ERR "%s unknown register: reg: %u", - __func__, reg); - return -EINVAL; - } - - uda134x_write_reg_cache(codec, reg, value); switch (reg) { case UDA134X_STATUS0: case UDA134X_STATUS1: addr = UDA134X_STATUS_ADDR; + data |= (reg - UDA134X_STATUS0) << 7; break; case UDA134X_DATA000: case UDA134X_DATA001: case UDA134X_DATA010: case UDA134X_DATA011: addr = UDA134X_DATA0_ADDR; + data |= (reg - UDA134X_DATA000) << 6; break; case UDA134X_DATA1: addr = UDA134X_DATA1_ADDR; @@ -133,27 +112,28 @@ static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg, static inline void uda134x_reset(struct snd_soc_codec *codec) { - u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0); - uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6)); + struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); + unsigned int mask = 1<<6; + + regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask); msleep(1); - uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6)); + regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0); } static int uda134x_mute(struct snd_soc_dai *dai, int mute) { - struct snd_soc_codec *codec = dai->codec; - u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010); + struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(dai->codec); + unsigned int mask = 1<<2; + unsigned int val; pr_debug("%s mute: %d\n", __func__, mute); if (mute) - mute_reg |= (1<<2); + val = mask; else - mute_reg &= ~(1<<2); + val = 0; - uda134x_write(codec, UDA134X_DATA010, mute_reg); - - return 0; + return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val); } static int uda134x_startup(struct snd_pcm_substream *substream, @@ -205,7 +185,7 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); - u8 hw_params; + unsigned int hw_params = 0; if (substream == uda134x->slave_substream) { pr_debug("%s ignoring hw_params for slave substream\n", @@ -213,10 +193,6 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream, return 0; } - hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0); - hw_params &= STATUS0_SYSCLK_MASK; - hw_params &= STATUS0_DAIFMT_MASK; - pr_debug("%s sysclk: %d, rate:%d\n", __func__, uda134x->sysclk, params_rate(params)); @@ -267,9 +243,8 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - uda134x_write(codec, UDA134X_STATUS0, hw_params); - - return 0; + return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, + STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params); } static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -324,10 +299,8 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, static int uda134x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct uda134x_platform_data *pd = codec->control_data; - int i; - u8 *cache = codec->reg_cache; - + struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); + struct uda134x_platform_data *pd = uda134x->pd; pr_debug("%s bias level %d\n", __func__, level); switch (level) { @@ -337,17 +310,17 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, /* power on */ if (pd->power) { pd->power(1); - /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++) - codec->driver->write(codec, i, *cache++); + regcache_sync(uda134x->regmap); } break; case SND_SOC_BIAS_STANDBY: break; case SND_SOC_BIAS_OFF: /* power off */ - if (pd->power) + if (pd->power) { pd->power(0); + regcache_mark_dirty(uda134x->regmap); + } break; } return 0; @@ -478,21 +451,14 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); - struct uda134x_priv *uda134x; - struct uda134x_platform_data *pd = codec->component.card->dev->platform_data; + struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); + struct uda134x_platform_data *pd = uda134x->pd; const struct snd_soc_dapm_widget *widgets; unsigned num_widgets; - int ret; printk(KERN_INFO "UDA134X SoC Audio Codec\n"); - if (!pd) { - printk(KERN_ERR "UDA134X SoC codec: " - "missing L3 bitbang function\n"); - return -ENODEV; - } - switch (pd->model) { case UDA134X_UDA1340: case UDA134X_UDA1341: @@ -506,13 +472,6 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) return -EINVAL; } - uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL); - if (uda134x == NULL) - return -ENOMEM; - snd_soc_codec_set_drvdata(codec, uda134x); - - codec->control_data = pd; - if (pd->power) pd->power(1); @@ -530,7 +489,6 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) if (ret) { printk(KERN_ERR "%s failed to register dapm controls: %d", __func__, ret); - kfree(uda134x); return ret; } @@ -551,36 +509,19 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) default: printk(KERN_ERR "%s unknown codec type: %d", __func__, pd->model); - kfree(uda134x); return -EINVAL; } if (ret < 0) { printk(KERN_ERR "UDA134X: failed to register controls\n"); - kfree(uda134x); return ret; } return 0; } -/* power down chip */ -static int uda134x_soc_remove(struct snd_soc_codec *codec) -{ - struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec); - - kfree(uda134x); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .probe = uda134x_soc_probe, - .remove = uda134x_soc_remove, - .reg_cache_size = sizeof(uda134x_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = uda134x_reg, - .reg_cache_step = 1, - .read = uda134x_read_reg_cache, .set_bias_level = uda134x_set_bias_level, .suspend_bias_off = true, @@ -590,8 +531,39 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), }; +static const struct regmap_config uda134x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = UDA134X_DATA1, + .reg_defaults = uda134x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults), + .cache_type = REGCACHE_RBTREE, + + .reg_write = uda134x_regmap_write, +}; + static int uda134x_codec_probe(struct platform_device *pdev) { + struct uda134x_platform_data *pd = pdev->dev.platform_data; + struct uda134x_priv *uda134x; + + if (!pd) { + dev_err(&pdev->dev, "Missing L3 bitbang function\n"); + return -ENODEV; + } + + uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL); + if (!uda134x) + return -ENOMEM; + + uda134x->pd = pd; + platform_set_drvdata(pdev, uda134x); + + uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd, + &uda134x_regmap_config); + if (IS_ERR(uda134x->regmap)) + return PTR_ERR(uda134x->regmap); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_uda134x, &uda134x_dai, 1); } diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h index 9faae06..e41ab38 100644 --- a/sound/soc/codecs/uda134x.h +++ b/sound/soc/codecs/uda134x.h @@ -26,8 +26,6 @@ #define UDA134X_DATA011 13 #define UDA134X_DATA1 14 -#define UDA134X_REGS_NUM 15 - #define STATUS0_DAIFMT_MASK (~(7<<1)) #define STATUS0_SYSCLK_MASK (~(3<<4)) diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 6e159f5..35f0469 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -269,12 +269,11 @@ static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1); * from -66 dB in 0.5 dB steps (2 dB steps, really) and * from -52 dB in 0.25 dB steps */ -static const unsigned int mvol_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(mvol_tlv, 0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1), 16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0), - 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0), -}; + 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0) +); /* * from -72 dB in 1.5 dB steps (6 dB steps really), @@ -282,13 +281,12 @@ static const unsigned int mvol_tlv[] = { * from -60 dB in 0.5 dB steps (2 dB steps really) and * from -46 dB in 0.25 dB steps */ -static const unsigned int vc_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(vc_tlv, 0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1), 8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0), 16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0), - 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0), -}; + 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0) +); /* from 0 to 6 dB in 2 dB steps if SPF mode != flat */ static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0); @@ -810,7 +808,6 @@ MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); static struct i2c_driver uda1380_i2c_driver = { .driver = { .name = "uda1380-codec", - .owner = THIS_MODULE, }, .probe = uda1380_i2c_probe, .remove = uda1380_i2c_remove, diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 6560a66..f2c6ad4 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -953,7 +953,7 @@ static int wm0010_spi_probe(struct spi_device *spi) trigger = IRQF_TRIGGER_FALLING; trigger |= IRQF_ONESHOT; - ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger | IRQF_ONESHOT, + ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger, "wm0010", wm0010); if (ret) { dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n", @@ -1003,7 +1003,6 @@ static int wm0010_spi_remove(struct spi_device *spi) static struct spi_driver wm0010_spi_driver = { .driver = { .name = "wm0010", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = wm0010_spi_probe, diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 048f005..ec45c5b 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -251,7 +251,6 @@ MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id); static struct i2c_driver wm1250_ev1_i2c_driver = { .driver = { .name = "wm1250-ev1", - .owner = THIS_MODULE, }, .probe = wm1250_ev1_probe, .remove = wm1250_ev1_remove, diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 21d5402..786abd0 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -942,7 +942,6 @@ MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id); static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", - .owner = THIS_MODULE, }, .probe = wm2000_i2c_probe, .remove = wm2000_i2c_remove, diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index c830832..fd1439e 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -166,7 +166,7 @@ static const struct wm_adsp_region wm2200_dsp2_regions[] = { { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE }, }; -static struct reg_default wm2200_reg_defaults[] = { +static const struct reg_default wm2200_reg_defaults[] = { { 0x000B, 0x0000 }, /* R11 - Tone Generator 1 */ { 0x0102, 0x0000 }, /* R258 - Clocking 3 */ { 0x0103, 0x0011 }, /* R259 - Clocking 4 */ @@ -897,7 +897,7 @@ static bool wm2200_readable_register(struct device *dev, unsigned int reg) } } -static const struct reg_default wm2200_reva_patch[] = { +static const struct reg_sequence wm2200_reva_patch[] = { { 0x07, 0x0003 }, { 0x102, 0x0200 }, { 0x203, 0x0084 }, @@ -1702,7 +1702,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream, int *bclk_rates; /* Data sizes if not using TDM */ - wl = snd_pcm_format_width(params_format(params)); + wl = params_width(params); if (wl < 0) return wl; fl = snd_soc_params_to_frame_size(params); @@ -2481,7 +2481,7 @@ static int wm2200_runtime_resume(struct device *dev) } #endif -static struct dev_pm_ops wm2200_pm = { +static const struct dev_pm_ops wm2200_pm = { SET_RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume, NULL) }; @@ -2495,7 +2495,6 @@ MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id); static struct i2c_driver wm2200_i2c_driver = { .driver = { .name = "wm2200", - .owner = THIS_MODULE, .pm = &wm2200_pm, }, .probe = wm2200_i2c_probe, diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 4c10cd8..c2cdcae 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1247,7 +1247,7 @@ static const struct snd_soc_dapm_route wm5100_dapm_routes[] = { { "PWM2", NULL, "PWM2 Driver" }, }; -static const struct reg_default wm5100_reva_patches[] = { +static const struct reg_sequence wm5100_reva_patches[] = { { WM5100_AUDIO_IF_1_10, 0 }, { WM5100_AUDIO_IF_1_11, 1 }, { WM5100_AUDIO_IF_1_12, 2 }, @@ -1408,7 +1408,7 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream, base = dai->driver->base; /* Data sizes if not using TDM */ - wl = snd_pcm_format_width(params_format(params)); + wl = params_width(params); if (wl < 0) return wl; fl = snd_soc_params_to_frame_size(params); @@ -2570,13 +2570,11 @@ static int wm5100_i2c_probe(struct i2c_client *i2c, if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, - wm5100_edge_irq, - irq_flags | IRQF_ONESHOT, + wm5100_edge_irq, irq_flags, "wm5100", wm5100); else ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq, - irq_flags | IRQF_ONESHOT, - "wm5100", + irq_flags, "wm5100", wm5100); if (ret != 0) { @@ -2708,7 +2706,7 @@ static int wm5100_runtime_resume(struct device *dev) } #endif -static struct dev_pm_ops wm5100_pm = { +static const struct dev_pm_ops wm5100_pm = { SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume, NULL) }; @@ -2722,7 +2720,6 @@ MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id); static struct i2c_driver wm5100_i2c_driver = { .driver = { .name = "wm5100", - .owner = THIS_MODULE, .pm = &wm5100_pm, }, .probe = wm5100_i2c_probe, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index d097f09e5..64637d1 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -788,8 +788,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), -SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, @@ -801,8 +800,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), -SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, @@ -814,8 +812,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), -SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2), SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, @@ -827,8 +824,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), -SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2), SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, @@ -851,10 +847,10 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), -SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), -SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), -SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE), @@ -1883,7 +1879,7 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) ret = snd_soc_add_codec_controls(codec, arizona_adsp2_rate_controls, 1); if (ret) - return ret; + goto err_adsp2_codec_probe; arizona_init_spk(codec); arizona_init_gpio(codec); @@ -1893,6 +1889,11 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) priv->core.arizona->dapm = dapm; return 0; + +err_adsp2_codec_probe: + wm_adsp2_codec_remove(&priv->core.adsp[0], codec); + + return ret; } static int wm5102_codec_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 709fcc6..9756578 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -131,6 +131,25 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = { { 0x33fb, 0xfe00 }, }; +static const struct reg_default wm5110_sysclk_reve_patch[] = { + { 0x3270, 0xE410 }, + { 0x3271, 0x3078 }, + { 0x3272, 0xE410 }, + { 0x3273, 0x3070 }, + { 0x3274, 0xE410 }, + { 0x3275, 0x3066 }, + { 0x3276, 0xE410 }, + { 0x3277, 0x3056 }, + { 0x327A, 0xE414 }, + { 0x327B, 0x3078 }, + { 0x327C, 0xE414 }, + { 0x327D, 0x3070 }, + { 0x327E, 0xE414 }, + { 0x327F, 0x3066 }, + { 0x3280, 0xE414 }, + { 0x3281, 0x3056 }, +}; + static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -146,7 +165,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch); break; default: - return 0; + patch = wm5110_sysclk_reve_patch; + patch_size = ARRAY_SIZE(wm5110_sysclk_reve_patch); + break; } switch (event) { @@ -164,6 +185,249 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +static const struct reg_sequence wm5110_no_dre_left_enable[] = { + { 0x3024, 0xE410 }, + { 0x3025, 0x0056 }, + { 0x301B, 0x0224 }, + { 0x301F, 0x4263 }, + { 0x3021, 0x5291 }, + { 0x3030, 0xE410 }, + { 0x3031, 0x3066 }, + { 0x3032, 0xE410 }, + { 0x3033, 0x3070 }, + { 0x3034, 0xE410 }, + { 0x3035, 0x3078 }, + { 0x3036, 0xE410 }, + { 0x3037, 0x3080 }, + { 0x3038, 0xE410 }, + { 0x3039, 0x3080 }, +}; + +static const struct reg_sequence wm5110_dre_left_enable[] = { + { 0x3024, 0x0231 }, + { 0x3025, 0x0B00 }, + { 0x301B, 0x0227 }, + { 0x301F, 0x4266 }, + { 0x3021, 0x5294 }, + { 0x3030, 0xE231 }, + { 0x3031, 0x0266 }, + { 0x3032, 0x8231 }, + { 0x3033, 0x4B15 }, + { 0x3034, 0x8231 }, + { 0x3035, 0x0B15 }, + { 0x3036, 0xE231 }, + { 0x3037, 0x5294 }, + { 0x3038, 0x0231 }, + { 0x3039, 0x0B00 }, +}; + +static const struct reg_sequence wm5110_no_dre_right_enable[] = { + { 0x3074, 0xE414 }, + { 0x3075, 0x0056 }, + { 0x306B, 0x0224 }, + { 0x306F, 0x4263 }, + { 0x3071, 0x5291 }, + { 0x3080, 0xE414 }, + { 0x3081, 0x3066 }, + { 0x3082, 0xE414 }, + { 0x3083, 0x3070 }, + { 0x3084, 0xE414 }, + { 0x3085, 0x3078 }, + { 0x3086, 0xE414 }, + { 0x3087, 0x3080 }, + { 0x3088, 0xE414 }, + { 0x3089, 0x3080 }, +}; + +static const struct reg_sequence wm5110_dre_right_enable[] = { + { 0x3074, 0x0231 }, + { 0x3075, 0x0B00 }, + { 0x306B, 0x0227 }, + { 0x306F, 0x4266 }, + { 0x3071, 0x5294 }, + { 0x3080, 0xE231 }, + { 0x3081, 0x0266 }, + { 0x3082, 0x8231 }, + { 0x3083, 0x4B17 }, + { 0x3084, 0x8231 }, + { 0x3085, 0x0B17 }, + { 0x3086, 0xE231 }, + { 0x3087, 0x5294 }, + { 0x3088, 0x0231 }, + { 0x3089, 0x0B00 }, +}; + +static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); + const struct reg_sequence *wseq; + int nregs; + + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + if (val & ARIZONA_DRE1L_ENA_MASK) { + wseq = wm5110_dre_left_enable; + nregs = ARRAY_SIZE(wm5110_dre_left_enable); + } else { + wseq = wm5110_no_dre_left_enable; + nregs = ARRAY_SIZE(wm5110_no_dre_left_enable); + priv->out_up_delay += 10; + } + break; + case ARIZONA_OUT1R_ENA_SHIFT: + if (val & ARIZONA_DRE1R_ENA_MASK) { + wseq = wm5110_dre_right_enable; + nregs = ARRAY_SIZE(wm5110_dre_right_enable); + } else { + wseq = wm5110_no_dre_right_enable; + nregs = ARRAY_SIZE(wm5110_no_dre_right_enable); + priv->out_up_delay += 10; + } + break; + default: + return 0; + } + + return regmap_multi_reg_write(arizona->regmap, wseq, nregs); +} + +static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); + + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + if (!(val & ARIZONA_DRE1L_ENA_MASK)) { + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG1, ARIZONA_WS_TRG1); + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG1, 0); + priv->out_down_delay += 27; + } + break; + case ARIZONA_OUT1R_ENA_SHIFT: + if (!(val & ARIZONA_DRE1R_ENA_MASK)) { + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG2, ARIZONA_WS_TRG2); + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG2, 0); + priv->out_down_delay += 27; + } + break; + default: + break; + } + + return 0; +} + +static int wm5110_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (priv->arizona->rev) { + case 0 ... 3: + break; + default: + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm5110_hp_pre_enable(w); + break; + case SND_SOC_DAPM_PRE_PMD: + wm5110_hp_pre_disable(w); + break; + default: + break; + } + break; + } + + return arizona_hp_ev(w, kcontrol, event); +} + +static int wm5110_clear_pga_volume(struct arizona *arizona, int output) +{ + struct reg_sequence clear_pga = { + ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80 + }; + int ret; + + ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1); + if (ret) + dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n", + clear_pga.reg, ret); + + return ret; +} + +static int wm5110_put_dre(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int ena, dre; + unsigned int mask = (0x1 << mc->shift) | (0x1 << mc->rshift); + unsigned int lnew = (!!ucontrol->value.integer.value[0]) << mc->shift; + unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift; + unsigned int lold, rold; + unsigned int lena, rena; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &ena); + if (ret) { + dev_err(arizona->dev, "Failed to read output state: %d\n", ret); + goto err; + } + ret = regmap_read(arizona->regmap, ARIZONA_DRE_ENABLE, &dre); + if (ret) { + dev_err(arizona->dev, "Failed to read DRE state: %d\n", ret); + goto err; + } + + lold = dre & (1 << mc->shift); + rold = dre & (1 << mc->rshift); + /* Enables are channel wise swapped from the DRE enables */ + lena = ena & (1 << mc->rshift); + rena = ena & (1 << mc->shift); + + if ((lena && lnew != lold) || (rena && rnew != rold)) { + dev_err(arizona->dev, "Can't change DRE on active outputs\n"); + ret = -EBUSY; + goto err; + } + + ret = regmap_update_bits(arizona->regmap, ARIZONA_DRE_ENABLE, + mask, lnew | rnew); + if (ret) { + dev_err(arizona->dev, "Failed to set DRE: %d\n", ret); + goto err; + } + + /* Force reset of PGA volumes, if turning DRE off */ + if (!lnew && lold) + wm5110_clear_pga_volume(arizona, mc->shift); + + if (!rnew && rold) + wm5110_clear_pga_volume(arizona, mc->rshift); + +err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); @@ -247,8 +511,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), -SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, @@ -260,8 +523,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), -SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, @@ -273,8 +535,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), -SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2), SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, @@ -286,8 +547,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), -SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2), SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, @@ -314,10 +574,10 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), -SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), -SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), -SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), @@ -409,12 +669,15 @@ SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), -SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, - ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0), -SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, - ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0), -SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, - ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0), +SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), +SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), +SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), @@ -904,11 +1167,11 @@ SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, - ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, - ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, @@ -1611,18 +1874,24 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) for (i = 0; i < WM5110_NUM_ADSP; ++i) { ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); if (ret) - return ret; + goto err_adsp2_codec_probe; } ret = snd_soc_add_codec_controls(codec, arizona_adsp2_rate_controls, WM5110_NUM_ADSP); if (ret) - return ret; + goto err_adsp2_codec_probe; snd_soc_dapm_disable_pin(dapm, "HAPTICS"); return 0; + +err_adsp2_codec_probe: + for (--i; i >= 0; --i) + wm_adsp2_codec_remove(&priv->core.adsp[i], codec); + + return ret; } static int wm5110_codec_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 41c62c1..ffbf3df 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -394,11 +394,10 @@ static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1); static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1); static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1); -static const unsigned int capture_sd_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(capture_sd_tlv, 0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1), - 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0), -}; + 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0) +); static const struct snd_kcontrol_new wm8350_snd_controls[] = { SOC_ENUM("Playback Deemphasis", wm8350_enum[0]), diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index d755508..b1d346a 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -370,10 +370,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } /* INMIX dB values */ -static const unsigned int in_mix_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0,7, TLV_DB_SCALE_ITEM(-1200, 600, 0), -}; +static const DECLARE_TLV_DB_SCALE(in_mix_tlv, -1200, 600, 0); /* Left In PGA Connections */ static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = { diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index dac5beb..b098a83 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -598,6 +598,7 @@ static const struct of_device_id wm8510_of_match[] = { { .compatible = "wlf,wm8510" }, { }, }; +MODULE_DEVICE_TABLE(of, wm8510_of_match); static const struct regmap_config wm8510_regmap = { .reg_bits = 7, @@ -690,7 +691,6 @@ MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); static struct i2c_driver wm8510_i2c_driver = { .driver = { .name = "wm8510", - .owner = THIS_MODULE, .of_match_table = wm8510_of_match, }, .probe = wm8510_i2c_probe, diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 43ea8ae..aa287a3 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -430,6 +430,7 @@ static const struct of_device_id wm8523_of_match[] = { { .compatible = "wlf,wm8523" }, { }, }; +MODULE_DEVICE_TABLE(of, wm8523_of_match); static const struct regmap_config wm8523_regmap = { .reg_bits = 8, @@ -534,7 +535,6 @@ MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id); static struct i2c_driver wm8523_i2c_driver = { .driver = { .name = "wm8523", - .owner = THIS_MODULE, .of_match_table = wm8523_of_match, }, .probe = wm8523_i2c_probe, diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 759a792..66602bf 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -916,6 +916,7 @@ static const struct of_device_id wm8580_of_match[] = { { .compatible = "wlf,wm8580" }, { }, }; +MODULE_DEVICE_TABLE(of, wm8580_of_match); static const struct regmap_config wm8580_regmap = { .reg_bits = 7, @@ -978,7 +979,6 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); static struct i2c_driver wm8580_i2c_driver = { .driver = { .name = "wm8580", - .owner = THIS_MODULE, .of_match_table = wm8580_of_match, }, .probe = wm8580_i2c_probe, diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index cc8251f..44b9e0a 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -478,7 +478,6 @@ MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); static struct i2c_driver wm8711_i2c_driver = { .driver = { .name = "wm8711", - .owner = THIS_MODULE, .of_match_table = wm8711_of_match, }, .probe = wm8711_i2c_probe, diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index f1a173e..cd7b024 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -319,7 +319,6 @@ MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); static struct i2c_driver wm8728_i2c_driver = { .driver = { .name = "wm8728", - .owner = THIS_MODULE, .of_match_table = wm8728_of_match, }, .probe = wm8728_i2c_probe, diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 915ea11..ace8645 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -79,12 +79,7 @@ static bool wm8731_volatile(struct device *dev, unsigned int reg) return reg == WM8731_RESET; } -static bool wm8731_writeable(struct device *dev, unsigned int reg) -{ - return reg <= WM8731_RESET; -} - -#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) +#define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0) static const char *wm8731_input_select[] = {"Line In", "Mic"}; @@ -496,8 +491,11 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - if (wm8731->mclk) - clk_prepare_enable(wm8731->mclk); + if (wm8731->mclk) { + ret = clk_prepare_enable(wm8731->mclk); + if (ret) + return ret; + } break; case SND_SOC_BIAS_PREPARE: break; @@ -571,69 +569,63 @@ static struct snd_soc_dai_driver wm8731_dai = { .symmetric_rates = 1, }; -static int wm8731_probe(struct snd_soc_codec *codec) +static int wm8731_request_supplies(struct device *dev, + struct wm8731_priv *wm8731) { - struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); int ret = 0, i; for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) wm8731->supplies[i].supply = wm8731_supply_names[i]; - ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) { - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + dev_err(dev, "Failed to request supplies: %d\n", ret); return ret; } ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) { - dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + dev_err(dev, "Failed to enable supplies: %d\n", ret); return ret; } - ret = wm8731_reset(codec); + return 0; +} + +static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731) +{ + int ret = 0; + + ret = wm8731_reset(wm8731->regmap); if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + dev_err(dev, "Failed to issue reset: %d\n", ret); goto err_regulator_enable; } - snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + /* Clear POWEROFF, keep everything else disabled */ + regmap_write(wm8731->regmap, WM8731_PWR, 0x7f); /* Latch the update bits */ - snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); - snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); - snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); - snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0); /* Disable bypass path by default */ - snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); + regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0); - /* Regulators will have been enabled by bias management */ - regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); - - return 0; + regcache_mark_dirty(wm8731->regmap); err_regulator_enable: + /* Regulators will be enabled by bias management */ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); return ret; } -/* power down chip */ -static int wm8731_remove(struct snd_soc_codec *codec) -{ - struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - - regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { - .probe = wm8731_probe, - .remove = wm8731_remove, .set_bias_level = wm8731_set_bias_level, .suspend_bias_off = true, @@ -658,7 +650,6 @@ static const struct regmap_config wm8731_regmap = { .max_register = WM8731_RESET, .volatile_reg = wm8731_volatile, - .writeable_reg = wm8731_writeable, .cache_type = REGCACHE_RBTREE, .reg_defaults = wm8731_reg_defaults, @@ -690,6 +681,12 @@ static int wm8731_spi_probe(struct spi_device *spi) mutex_init(&wm8731->lock); + spi_set_drvdata(spi, wm8731); + + ret = wm8731_request_supplies(&spi->dev, wm8731); + if (ret != 0) + return ret; + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); @@ -698,7 +695,9 @@ static int wm8731_spi_probe(struct spi_device *spi) return ret; } - spi_set_drvdata(spi, wm8731); + ret = wm8731_hw_init(&spi->dev, wm8731); + if (ret != 0) + return ret; ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_wm8731, &wm8731_dai, 1); @@ -754,6 +753,12 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, mutex_init(&wm8731->lock); + i2c_set_clientdata(i2c, wm8731); + + ret = wm8731_request_supplies(&i2c->dev, wm8731); + if (ret != 0) + return ret; + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); @@ -762,7 +767,9 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, return ret; } - i2c_set_clientdata(i2c, wm8731); + ret = wm8731_hw_init(&i2c->dev, wm8731); + if (ret != 0) + return ret; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8731, &wm8731_dai, 1); @@ -789,7 +796,6 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); static struct i2c_driver wm8731_i2c_driver = { .driver = { .name = "wm8731", - .owner = THIS_MODULE, .of_match_table = wm8731_of_match, }, .probe = wm8731_i2c_probe, diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 6ad606f..e4a03d9 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -79,13 +79,12 @@ static int wm8737_reset(struct snd_soc_codec *codec) return snd_soc_write(codec, WM8737_RESET, 0); } -static const unsigned int micboost_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(micboost_tlv, 0, 0, TLV_DB_SCALE_ITEM(1300, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2800, 0, 0), - 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0) +); static const DECLARE_TLV_DB_SCALE(pga_tlv, -9750, 50, 1); static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(ng_tlv, -7800, 600, 0); @@ -657,7 +656,6 @@ MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id); static struct i2c_driver wm8737_i2c_driver = { .driver = { .name = "wm8737", - .owner = THIS_MODULE, .of_match_table = wm8737_of_match, }, .probe = wm8737_i2c_probe, diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index b346237..de42c03 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -61,25 +61,6 @@ static const struct reg_default wm8741_reg_defaults[] = { { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */ }; -static bool wm8741_readable(struct device *dev, unsigned int reg) -{ - switch (reg) { - case WM8741_DACLLSB_ATTENUATION: - case WM8741_DACLMSB_ATTENUATION: - case WM8741_DACRLSB_ATTENUATION: - case WM8741_DACRMSB_ATTENUATION: - case WM8741_VOLUME_CONTROL: - case WM8741_FORMAT_CONTROL: - case WM8741_FILTER_CONTROL: - case WM8741_MODE_CONTROL_1: - case WM8741_MODE_CONTROL_2: - case WM8741_ADDITIONAL_CONTROL_1: - return true; - default: - return false; - } -} - static int wm8741_reset(struct snd_soc_codec *codec) { return snd_soc_write(codec, WM8741_RESET, 0); @@ -278,51 +259,38 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, switch (freq) { case 0: wm8741->sysclk_constraints = NULL; - wm8741->sysclk = freq; - return 0; - + break; case 11289600: wm8741->sysclk_constraints = &constraints_11289; - wm8741->sysclk = freq; - return 0; - + break; case 12288000: wm8741->sysclk_constraints = &constraints_12288; - wm8741->sysclk = freq; - return 0; - + break; case 16384000: wm8741->sysclk_constraints = &constraints_16384; - wm8741->sysclk = freq; - return 0; - + break; case 16934400: wm8741->sysclk_constraints = &constraints_16934; - wm8741->sysclk = freq; - return 0; - + break; case 18432000: wm8741->sysclk_constraints = &constraints_18432; - wm8741->sysclk = freq; - return 0; - + break; case 22579200: case 33868800: wm8741->sysclk_constraints = &constraints_22579; - wm8741->sysclk = freq; - return 0; - + break; case 24576000: wm8741->sysclk_constraints = &constraints_24576; - wm8741->sysclk = freq; - return 0; - + break; case 36864000: wm8741->sysclk_constraints = &constraints_36864; - wm8741->sysclk = freq; - return 0; + break; + default: + return -EINVAL; } - return -EINVAL; + + wm8741->sysclk = freq; + return 0; } static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, @@ -554,8 +522,6 @@ static const struct regmap_config wm8741_regmap = { .reg_defaults = wm8741_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults), .cache_type = REGCACHE_RBTREE, - - .readable_reg = wm8741_readable, }; static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) @@ -633,7 +599,6 @@ MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id); static struct i2c_driver wm8741_i2c_driver = { .driver = { .name = "wm8741", - .owner = THIS_MODULE, .of_match_table = wm8741_of_match, }, .probe = wm8741_i2c_probe, diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 56d89b0..873933a 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -826,7 +826,6 @@ MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); static struct i2c_driver wm8750_i2c_driver = { .driver = { .name = "wm8750", - .owner = THIS_MODULE, .of_match_table = wm8750_of_match, }, .probe = wm8750_i2c_probe, diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index feb2997a..a801c6d 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -138,11 +138,6 @@ static bool wm8753_volatile(struct device *dev, unsigned int reg) return reg == WM8753_RESET; } -static bool wm8753_writeable(struct device *dev, unsigned int reg) -{ - return reg <= WM8753_ADCTL2; -} - /* codec private data */ struct wm8753_priv { struct regmap *regmap; @@ -276,12 +271,11 @@ static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0); static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); -static const unsigned int out_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(out_tlv, /* 0000000 - 0101111 = "Analogue mute" */ 0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0), - 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0), -}; + 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0) +); static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0); static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0); @@ -1510,7 +1504,6 @@ static const struct regmap_config wm8753_regmap = { .val_bits = 9, .max_register = WM8753_ADCTL2, - .writeable_reg = wm8753_writeable, .volatile_reg = wm8753_volatile, .cache_type = REGCACHE_RBTREE, @@ -1609,7 +1602,6 @@ MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id); static struct i2c_driver wm8753_i2c_driver = { .driver = { .name = "wm8753", - .owner = THIS_MODULE, .of_match_table = wm8753_of_match, }, .probe = wm8753_i2c_probe, diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index ece9b44..183c9a4 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -265,7 +265,7 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream, } /* Set word length */ - switch (snd_pcm_format_width(params_format(params))) { + switch (params_width(params)) { case 16: iface = 0; break; @@ -280,7 +280,7 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream, break; default: dev_err(codec->dev, "Unsupported sample size: %i\n", - snd_pcm_format_width(params_format(params))); + params_width(params)); return -EINVAL; } @@ -536,7 +536,6 @@ MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id); static struct i2c_driver wm8776_i2c_driver = { .driver = { .name = "wm8776", - .owner = THIS_MODULE, .of_match_table = wm8776_of_match, }, .probe = wm8776_i2c_probe, diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index 6596f5f..f27464c 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -49,7 +49,6 @@ MODULE_DEVICE_TABLE(of, wm8804_of_match); static struct i2c_driver wm8804_i2c_driver = { .driver = { .name = "wm8804", - .owner = THIS_MODULE, .pm = &wm8804_pm, .of_match_table = wm8804_of_match, }, diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index c195c2e..8d91470 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -98,7 +98,7 @@ WM8804_REGULATOR_EVENT(0) WM8804_REGULATOR_EVENT(1) static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; -static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); +static SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); static const struct snd_kcontrol_new wm8804_tx_source_mux[] = { SOC_DAPM_ENUM_EXT("Input Source", txsrc, diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index f3759ec..98900aa 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1312,7 +1312,6 @@ MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id); static struct i2c_driver wm8900_i2c_driver = { .driver = { .name = "wm8900", - .owner = THIS_MODULE, }, .probe = wm8900_i2c_probe, .remove = wm8900_i2c_remove, diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index b5322c1..b011253 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2193,7 +2193,6 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); static struct i2c_driver wm8903_i2c_driver = { .driver = { .name = "wm8903", - .owner = THIS_MODULE, .of_match_table = wm8903_of_match, }, .probe = wm8903_i2c_probe, diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 265a4a5..b783743 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1837,7 +1837,9 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - clk_prepare_enable(wm8904->mclk); + ret = clk_prepare_enable(wm8904->mclk); + if (ret) + return ret; break; case SND_SOC_BIAS_PREPARE: @@ -2292,7 +2294,6 @@ MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id); static struct i2c_driver wm8904_i2c_driver = { .driver = { .name = "wm8904", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(wm8904_of_match), }, .probe = wm8904_i2c_probe, diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 98ef0ba..f6f9395 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -787,7 +787,6 @@ MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); static struct i2c_driver wm8940_i2c_driver = { .driver = { .name = "wm8940", - .owner = THIS_MODULE, }, .probe = wm8940_i2c_probe, .remove = wm8940_i2c_remove, diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 2d591c2..12e4435 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -1009,7 +1009,6 @@ MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id); static struct i2c_driver wm8955_i2c_driver = { .driver = { .name = "wm8955", - .owner = THIS_MODULE, }, .probe = wm8955_i2c_probe, .remove = wm8955_i2c_remove, diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 94c5c46..e3b7d0c 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -48,6 +48,9 @@ #define WM8960_DISOP 0x40 #define WM8960_DRES_MASK 0x30 +static bool is_pll_freq_available(unsigned int source, unsigned int target); +static int wm8960_set_pll(struct snd_soc_codec *codec, + unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache * We can't read the WM8960 register space when we are @@ -126,9 +129,12 @@ struct wm8960_priv { struct snd_soc_dapm_widget *rout1; struct snd_soc_dapm_widget *out3; bool deemph; - int playback_fs; + int lrclk; int bclk; int sysclk; + int clk_id; + int freq_in; + bool is_stream_in_use[2]; struct wm8960_data pdata; }; @@ -164,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) if (wm8960->deemph) { best = 1; for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { - if (abs(deemph_settings[i] - wm8960->playback_fs) < - abs(deemph_settings[best] - wm8960->playback_fs)) + if (abs(deemph_settings[i] - wm8960->lrclk) < + abs(deemph_settings[best] - wm8960->lrclk)) best = i; } @@ -565,6 +571,9 @@ static struct { { 8000, 5 }, }; +/* -1 for reserved value */ +static const int sysclk_divs[] = { 1, -1, 2, -1 }; + /* Multiply 256 for internal 256 div */ static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; @@ -574,61 +583,110 @@ static const int bclk_divs[] = { 120, 160, 220, 240, 320, 320, 320 }; -static void wm8960_configure_clocking(struct snd_soc_codec *codec, - bool tx, int lrclk) +static int wm8960_configure_clocking(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + int sysclk, bclk, lrclk, freq_out, freq_in; u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); - u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); - u32 sysclk; - int i, j; + int i, j, k; if (!(iface1 & (1<<6))) { dev_dbg(codec->dev, "Codec is slave mode, no need to configure clock\n"); - return; + return 0; + } + + if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { + dev_err(codec->dev, "No MCLK configured\n"); + return -EINVAL; } - if (!wm8960->sysclk) { - dev_dbg(codec->dev, "No SYSCLK configured\n"); - return; + freq_in = wm8960->freq_in; + bclk = wm8960->bclk; + lrclk = wm8960->lrclk; + /* + * If it's sysclk auto mode, check if the MCLK can provide sysclk or + * not. If MCLK can provide sysclk, using MCLK to provide sysclk + * directly. Otherwise, auto select a available pll out frequency + * and set PLL. + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { + /* disable the PLL and using MCLK to provide sysclk */ + wm8960_set_pll(codec, 0, 0); + freq_out = freq_in; + } else if (wm8960->sysclk) { + freq_out = wm8960->sysclk; + } else { + dev_err(codec->dev, "No SYSCLK configured\n"); + return -EINVAL; } - if (!wm8960->bclk || !lrclk) { - dev_dbg(codec->dev, "No audio clocks configured\n"); - return; + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk = freq_out / sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + if (sysclk == dac_divs[j] * lrclk) { + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) + if (sysclk == bclk * bclk_divs[k] / 10) + break; + if (k != ARRAY_SIZE(bclk_divs)) + break; + } + } + if (j != ARRAY_SIZE(dac_divs)) + break; } - for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { - if (wm8960->sysclk == lrclk * dac_divs[i]) { - for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { - sysclk = wm8960->bclk * bclk_divs[j] / 10; - if (wm8960->sysclk == sysclk) + if (i != ARRAY_SIZE(sysclk_divs)) { + goto configure_clock; + } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; + } + /* get a available pll out frequency and set pll */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + sysclk = lrclk * dac_divs[j]; + freq_out = sysclk * sysclk_divs[i]; + + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { + if (sysclk == bclk * bclk_divs[k] / 10 && + is_pll_freq_available(freq_in, freq_out)) { + wm8960_set_pll(codec, + freq_in, freq_out); break; + } else { + continue; + } } - if(j != ARRAY_SIZE(bclk_divs)) + if (k != ARRAY_SIZE(bclk_divs)) break; } + if (j != ARRAY_SIZE(dac_divs)) + break; } - if (i == ARRAY_SIZE(dac_divs)) { - dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); - return; + if (i == ARRAY_SIZE(sysclk_divs)) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; } - /* - * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC - * pin is used as a frame clock for ADCs and DACs. - */ - if (iface2 & (1<<6)) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); - else if (tx) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); - else if (!tx) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); +configure_clock: + /* configure sysclk clock */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); + + /* configure frame clock */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ - snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); + snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); + + return 0; } static int wm8960_hw_params(struct snd_pcm_substream *substream, @@ -667,9 +725,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - wm8960->playback_fs = params_rate(params); + if (tx) { wm8960_set_deemph(codec); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) @@ -682,7 +740,23 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); - wm8960_configure_clocking(codec, tx, params_rate(params)); + wm8960->is_stream_in_use[tx] = true; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON && + !wm8960->is_stream_in_use[!tx]) + return wm8960_configure_clocking(codec); + + return 0; +} + +static int wm8960_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + wm8960->is_stream_in_use[tx] = false; return 0; } @@ -702,6 +776,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int ret; switch (level) { @@ -721,11 +796,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, } } + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + /* Set VMID to 2x50k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(codec, 0, 0); + if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); break; @@ -780,6 +866,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int reg, ret; switch (level) { @@ -831,9 +918,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, return ret; } } + + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + break; case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(codec, 0, 0); + if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); @@ -892,6 +991,28 @@ struct _pll_div { u32 k:24; }; +static bool is_pll_freq_available(unsigned int source, unsigned int target) +{ + unsigned int Ndiv; + + if (source == 0 || target == 0) + return false; + + /* Scale up target to PLL operating frequency */ + target *= 4; + Ndiv = target / source; + + if (Ndiv < 6) { + source >>= 1; + Ndiv = target / source; + } + + if ((Ndiv < 6) || (Ndiv > 12)) + return false; + + return true; +} + /* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10) @@ -943,10 +1064,9 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } -static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - int source, unsigned int freq_in, unsigned int freq_out) +static int wm8960_set_pll(struct snd_soc_codec *codec, + unsigned int freq_in, unsigned int freq_out) { - struct snd_soc_codec *codec = codec_dai->codec; u16 reg; static struct _pll_div pll_div; int ret; @@ -986,6 +1106,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } +static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + wm8960->freq_in = freq_in; + + if (pll_id == WM8960_SYSCLK_AUTO) + return 0; + + return wm8960_set_pll(codec, freq_in, freq_out); +} + static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { @@ -1043,11 +1177,14 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; + case WM8960_SYSCLK_AUTO: + break; default: return -EINVAL; } wm8960->sysclk = freq; + wm8960->clk_id = clk_id; return 0; } @@ -1060,6 +1197,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, + .hw_free = wm8960_hw_free, .digital_mute = wm8960_mute, .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, @@ -1216,7 +1354,6 @@ MODULE_DEVICE_TABLE(of, wm8960_of_match); static struct i2c_driver wm8960_i2c_driver = { .driver = { .name = "wm8960", - .owner = THIS_MODULE, .of_match_table = wm8960_of_match, }, .probe = wm8960_i2c_probe, diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h index 2d8163d..ab3220d 100644 --- a/sound/soc/codecs/wm8960.h +++ b/sound/soc/codecs/wm8960.h @@ -82,6 +82,7 @@ #define WM8960_SYSCLK_MCLK (0 << 0) #define WM8960_SYSCLK_PLL (1 << 0) +#define WM8960_SYSCLK_AUTO (2 << 0) #define WM8960_DAC_DIV_1 (0 << 3) #define WM8960_DAC_DIV_1_5 (1 << 3) diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index a057662..e30446a 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -331,13 +331,12 @@ static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0); static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); -static unsigned int boost_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(boost_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0), - 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0) +); static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0); static const struct snd_kcontrol_new wm8961_snd_controls[] = { @@ -982,7 +981,6 @@ MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); static struct i2c_driver wm8961_i2c_driver = { .driver = { .name = "wm8961", - .owner = THIS_MODULE, }, .probe = wm8961_i2c_probe, .remove = wm8961_i2c_remove, diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index c5748fd..b4eb975 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -113,7 +113,7 @@ WM8962_REGULATOR_EVENT(5) WM8962_REGULATOR_EVENT(6) WM8962_REGULATOR_EVENT(7) -static struct reg_default wm8962_reg[] = { +static const struct reg_default wm8962_reg[] = { { 0, 0x009F }, /* R0 - Left Input volume */ { 1, 0x049F }, /* R1 - Right Input volume */ { 2, 0x0000 }, /* R2 - HPOUTL volume */ @@ -1456,14 +1456,13 @@ static int wm8962_reset(struct wm8962_priv *wm8962) static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0); static const DECLARE_TLV_DB_SCALE(mixin_tlv, -1500, 300, 0); -static const unsigned int mixinpga_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static const DECLARE_TLV_DB_RANGE(mixinpga_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0), 2, 2, TLV_DB_SCALE_ITEM(1300, 1300, 0), 3, 4, TLV_DB_SCALE_ITEM(1800, 200, 0), 5, 5, TLV_DB_SCALE_ITEM(2400, 0, 0), - 6, 7, TLV_DB_SCALE_ITEM(2700, 300, 0), -}; + 6, 7, TLV_DB_SCALE_ITEM(2700, 300, 0) +); static const DECLARE_TLV_DB_SCALE(beep_tlv, -9600, 600, 1); static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0); @@ -1471,11 +1470,10 @@ static const DECLARE_TLV_DB_SCALE(inmix_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); static const DECLARE_TLV_DB_SCALE(hp_tlv, -700, 100, 0); -static const unsigned int classd_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(classd_tlv, 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), - 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0), -}; + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0) +); static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static int wm8962_dsp2_write_config(struct snd_soc_codec *codec) @@ -3495,7 +3493,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = { }; /* Improve power consumption for IN4 DC measurement mode */ -static const struct reg_default wm8962_dc_measure[] = { +static const struct reg_sequence wm8962_dc_measure[] = { { 0xfd, 0x1 }, { 0xcc, 0x40 }, { 0xfd, 0 }, @@ -3859,7 +3857,7 @@ static int wm8962_runtime_suspend(struct device *dev) } #endif -static struct dev_pm_ops wm8962_pm = { +static const struct dev_pm_ops wm8962_pm = { SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL) }; @@ -3878,7 +3876,6 @@ MODULE_DEVICE_TABLE(of, wm8962_of_match); static struct i2c_driver wm8962_i2c_driver = { .driver = { .name = "wm8962", - .owner = THIS_MODULE, .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index b51184c..2cdde32 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -710,7 +710,6 @@ MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id); static struct i2c_driver wm8971_i2c_driver = { .driver = { .name = "wm8971", - .owner = THIS_MODULE, }, .probe = wm8971_i2c_probe, .remove = wm8971_i2c_remove, diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 33b16a7..0a60677 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -634,7 +634,6 @@ MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); static struct i2c_driver wm8974_i2c_driver = { .driver = { .name = "wm8974", - .owner = THIS_MODULE, }, .probe = wm8974_i2c_probe, .remove = wm8974_i2c_remove, diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index cfc8cdf..d36d600 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1072,7 +1072,6 @@ MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id); static struct i2c_driver wm8978_i2c_driver = { .driver = { .name = "wm8978", - .owner = THIS_MODULE, }, .probe = wm8978_i2c_probe, .remove = wm8978_i2c_remove, diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 2fdd2c6..f3193fb 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -84,66 +84,6 @@ static const struct reg_default wm8983_defaults[] = { { 0x3D, 0x0000 }, /* R61 - BIAS CTRL */ }; -static const struct wm8983_reg_access { - u16 read; /* Mask of readable bits */ - u16 write; /* Mask of writable bits */ -} wm8983_access_masks[WM8983_MAX_REGISTER + 1] = { - [0x00] = { 0x0000, 0x01FF }, /* R0 - Software Reset */ - [0x01] = { 0x0000, 0x01FF }, /* R1 - Power management 1 */ - [0x02] = { 0x0000, 0x01FF }, /* R2 - Power management 2 */ - [0x03] = { 0x0000, 0x01EF }, /* R3 - Power management 3 */ - [0x04] = { 0x0000, 0x01FF }, /* R4 - Audio Interface */ - [0x05] = { 0x0000, 0x003F }, /* R5 - Companding control */ - [0x06] = { 0x0000, 0x01FD }, /* R6 - Clock Gen control */ - [0x07] = { 0x0000, 0x000F }, /* R7 - Additional control */ - [0x08] = { 0x0000, 0x003F }, /* R8 - GPIO Control */ - [0x09] = { 0x0000, 0x0070 }, /* R9 - Jack Detect Control 1 */ - [0x0A] = { 0x0000, 0x004F }, /* R10 - DAC Control */ - [0x0B] = { 0x0000, 0x01FF }, /* R11 - Left DAC digital Vol */ - [0x0C] = { 0x0000, 0x01FF }, /* R12 - Right DAC digital vol */ - [0x0D] = { 0x0000, 0x00FF }, /* R13 - Jack Detect Control 2 */ - [0x0E] = { 0x0000, 0x01FB }, /* R14 - ADC Control */ - [0x0F] = { 0x0000, 0x01FF }, /* R15 - Left ADC Digital Vol */ - [0x10] = { 0x0000, 0x01FF }, /* R16 - Right ADC Digital Vol */ - [0x12] = { 0x0000, 0x017F }, /* R18 - EQ1 - low shelf */ - [0x13] = { 0x0000, 0x017F }, /* R19 - EQ2 - peak 1 */ - [0x14] = { 0x0000, 0x017F }, /* R20 - EQ3 - peak 2 */ - [0x15] = { 0x0000, 0x017F }, /* R21 - EQ4 - peak 3 */ - [0x16] = { 0x0000, 0x007F }, /* R22 - EQ5 - high shelf */ - [0x18] = { 0x0000, 0x01FF }, /* R24 - DAC Limiter 1 */ - [0x19] = { 0x0000, 0x007F }, /* R25 - DAC Limiter 2 */ - [0x1B] = { 0x0000, 0x01FF }, /* R27 - Notch Filter 1 */ - [0x1C] = { 0x0000, 0x017F }, /* R28 - Notch Filter 2 */ - [0x1D] = { 0x0000, 0x017F }, /* R29 - Notch Filter 3 */ - [0x1E] = { 0x0000, 0x017F }, /* R30 - Notch Filter 4 */ - [0x20] = { 0x0000, 0x01BF }, /* R32 - ALC control 1 */ - [0x21] = { 0x0000, 0x00FF }, /* R33 - ALC control 2 */ - [0x22] = { 0x0000, 0x01FF }, /* R34 - ALC control 3 */ - [0x23] = { 0x0000, 0x000F }, /* R35 - Noise Gate */ - [0x24] = { 0x0000, 0x001F }, /* R36 - PLL N */ - [0x25] = { 0x0000, 0x003F }, /* R37 - PLL K 1 */ - [0x26] = { 0x0000, 0x01FF }, /* R38 - PLL K 2 */ - [0x27] = { 0x0000, 0x01FF }, /* R39 - PLL K 3 */ - [0x29] = { 0x0000, 0x000F }, /* R41 - 3D control */ - [0x2A] = { 0x0000, 0x01E7 }, /* R42 - OUT4 to ADC */ - [0x2B] = { 0x0000, 0x01BF }, /* R43 - Beep control */ - [0x2C] = { 0x0000, 0x0177 }, /* R44 - Input ctrl */ - [0x2D] = { 0x0000, 0x01FF }, /* R45 - Left INP PGA gain ctrl */ - [0x2E] = { 0x0000, 0x01FF }, /* R46 - Right INP PGA gain ctrl */ - [0x2F] = { 0x0000, 0x0177 }, /* R47 - Left ADC BOOST ctrl */ - [0x30] = { 0x0000, 0x0177 }, /* R48 - Right ADC BOOST ctrl */ - [0x31] = { 0x0000, 0x007F }, /* R49 - Output ctrl */ - [0x32] = { 0x0000, 0x01FF }, /* R50 - Left mixer ctrl */ - [0x33] = { 0x0000, 0x01FF }, /* R51 - Right mixer ctrl */ - [0x34] = { 0x0000, 0x01FF }, /* R52 - LOUT1 (HP) volume ctrl */ - [0x35] = { 0x0000, 0x01FF }, /* R53 - ROUT1 (HP) volume ctrl */ - [0x36] = { 0x0000, 0x01FF }, /* R54 - LOUT2 (SPK) volume ctrl */ - [0x37] = { 0x0000, 0x01FF }, /* R55 - ROUT2 (SPK) volume ctrl */ - [0x38] = { 0x0000, 0x004F }, /* R56 - OUT3 mixer ctrl */ - [0x39] = { 0x0000, 0x00FF }, /* R57 - OUT4 (MONO) mix ctrl */ - [0x3D] = { 0x0000, 0x0100 } /* R61 - BIAS CTRL */ -}; - /* vol/gain update regs */ static const int vol_update_regs[] = { WM8983_LEFT_DAC_DIGITAL_VOL, @@ -605,12 +545,19 @@ static int eqmode_put(struct snd_kcontrol *kcontrol, return 0; } -static bool wm8983_readable(struct device *dev, unsigned int reg) +static bool wm8983_writeable(struct device *dev, unsigned int reg) { - if (reg > WM8983_MAX_REGISTER) - return 0; - - return wm8983_access_masks[reg].read != 0; + switch (reg) { + case WM8983_SOFTWARE_RESET ... WM8983_RIGHT_ADC_DIGITAL_VOL: + case WM8983_EQ1_LOW_SHELF ... WM8983_DAC_LIMITER_2: + case WM8983_NOTCH_FILTER_1 ... WM8983_NOTCH_FILTER_4: + case WM8983_ALC_CONTROL_1 ... WM8983_PLL_K_3: + case WM8983_3D_CONTROL ... WM8983_OUT4_MONO_MIX_CTRL: + case WM8983_BIAS_CTRL: + return true; + default: + return false; + } } static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute) @@ -1048,8 +995,9 @@ static const struct regmap_config wm8983_regmap = { .reg_defaults = wm8983_defaults, .num_reg_defaults = ARRAY_SIZE(wm8983_defaults), .cache_type = REGCACHE_RBTREE, + .max_register = WM8983_MAX_REGISTER, - .readable_reg = wm8983_readable, + .writeable_reg = wm8983_writeable, }; #if defined(CONFIG_SPI_MASTER) @@ -1133,7 +1081,6 @@ MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id); static struct i2c_driver wm8983_i2c_driver = { .driver = { .name = "wm8983", - .owner = THIS_MODULE, }, .probe = wm8983_i2c_probe, .remove = wm8983_i2c_remove, diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 8a85f50..9c3c151 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1144,7 +1144,6 @@ MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id); static struct i2c_driver wm8985_i2c_driver = { .driver = { .name = "wm8985", - .owner = THIS_MODULE, }, .probe = wm8985_i2c_probe, .remove = wm8985_i2c_remove, diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index f13a995..c88ce99 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -919,7 +919,6 @@ MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id); static struct i2c_driver wm8988_i2c_driver = { .driver = { .name = "wm8988", - .owner = THIS_MODULE, }, .probe = wm8988_i2c_probe, .remove = wm8988_i2c_remove, diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 1993fd2..23ecd30 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -418,10 +418,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w, } /* INMIX dB values */ -static const unsigned int in_mix_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 7, TLV_DB_SCALE_ITEM(-1200, 600, 0), -}; +static const DECLARE_TLV_DB_SCALE(in_mix_tlv, -1200, 600, 0); /* Left In PGA Connections */ static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = { @@ -1356,7 +1353,6 @@ MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id); static struct i2c_driver wm8990_i2c_driver = { .driver = { .name = "wm8990", - .owner = THIS_MODULE, }, .probe = wm8990_i2c_probe, .remove = wm8990_i2c_remove, diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 44a6777..c9ee0ac 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -111,45 +111,14 @@ static bool wm8991_volatile(struct device *dev, unsigned int reg) } } -static const unsigned int rec_mix_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 7, TLV_DB_LINEAR_ITEM(-1500, 600), -}; - -static const unsigned int in_pga_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000), -}; - -static const unsigned int out_mix_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 7, TLV_DB_LINEAR_ITEM(0, -2100), -}; - -static const unsigned int out_pga_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 127, TLV_DB_LINEAR_ITEM(-7300, 600), -}; - -static const unsigned int out_omix_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 7, TLV_DB_LINEAR_ITEM(-600, 0), -}; - -static const unsigned int out_dac_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 255, TLV_DB_LINEAR_ITEM(-7163, 0), -}; - -static const unsigned int in_adc_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763), -}; - -static const unsigned int out_sidetone_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 31, TLV_DB_LINEAR_ITEM(-3600, 0), -}; +static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); +static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000); +static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100); +static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600); +static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0); +static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0); +static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763); +static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0); static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -429,10 +398,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w, } /* INMIX dB values */ -static const unsigned int in_mix_tlv[] = { - TLV_DB_RANGE_HEAD(1), - 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600), -}; +static const DECLARE_TLV_DB_LINEAR(in_mix_tlv, -1200, 600); /* Left In PGA Connections */ static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = { @@ -1363,7 +1329,6 @@ MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id); static struct i2c_driver wm8991_i2c_driver = { .driver = { .name = "wm8991", - .owner = THIS_MODULE, }, .probe = wm8991_i2c_probe, .remove = wm8991_i2c_remove, diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 8a8db86..8668c4c 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -41,7 +41,7 @@ static const char *wm8993_supply_names[WM8993_NUM_SUPPLIES] = { "SPKVDD", }; -static struct reg_default wm8993_reg_defaults[] = { +static const struct reg_default wm8993_reg_defaults[] = { { 1, 0x0000 }, /* R1 - Power Management (1) */ { 2, 0x6000 }, /* R2 - Power Management (2) */ { 3, 0x0000 }, /* R3 - Power Management (3) */ @@ -628,11 +628,10 @@ static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0); static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0); static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0); -static const unsigned int drc_max_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(drc_max_tlv, 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), - 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0) +); static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0); static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); @@ -1595,7 +1594,7 @@ static int wm8993_resume(struct snd_soc_codec *codec) #endif /* Tune DC servo configuration */ -static struct reg_default wm8993_regmap_patch[] = { +static const struct reg_sequence wm8993_regmap_patch[] = { { 0x44, 3 }, { 0x56, 3 }, { 0x44, 0 }, @@ -1742,7 +1741,6 @@ MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id); static struct i2c_driver wm8993_i2c_driver = { .driver = { .name = "wm8993", - .owner = THIS_MODULE, }, .probe = wm8993_i2c_probe, .remove = wm8993_i2c_remove, diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 962e1d3..2ccbb32 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1942,14 +1942,16 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2ADCDAT", NULL, "AIF2ADC Mux" }, /* AIF3 output */ - { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1L" }, - { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1R" }, - { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2L" }, - { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2R" }, - { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCL" }, - { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCR" }, - { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" }, - { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC1L" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC1R" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC2L" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC2R" }, + { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, + { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, + { "AIF3ADC Mux", "AIF2DACDAT", "AIF2DACL" }, + { "AIF3ADC Mux", "AIF2DACDAT", "AIF2DACR" }, + + { "AIF3ADCDAT", NULL, "AIF3ADC Mux" }, /* Loopback */ { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" }, diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 505b65f..eda52a9 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2298,7 +2298,6 @@ MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id); static struct i2c_driver wm8995_i2c_driver = { .driver = { .name = "wm8995", - .owner = THIS_MODULE, }, .probe = wm8995_i2c_probe, .remove = wm8995_i2c_remove, diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 3dd063f..f7ccd9f 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -117,7 +117,7 @@ WM8996_REGULATOR_EVENT(0) WM8996_REGULATOR_EVENT(1) WM8996_REGULATOR_EVENT(2) -static struct reg_default wm8996_reg[] = { +static const struct reg_default wm8996_reg[] = { { WM8996_POWER_MANAGEMENT_1, 0x0 }, { WM8996_POWER_MANAGEMENT_2, 0x0 }, { WM8996_POWER_MANAGEMENT_3, 0x0 }, @@ -1780,7 +1780,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream, wm8996->rx_rate[dai->id] = params_rate(params); /* Needs looking at for TDM */ - bits = snd_pcm_format_width(params_format(params)); + bits = params_width(params); if (bits < 0) return bits; aifdata |= (bits << WM8996_AIF1TX_WL_SHIFT) | bits; @@ -2647,12 +2647,10 @@ static int wm8996_probe(struct snd_soc_codec *codec) if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, wm8996_edge_irq, - irq_flags | IRQF_ONESHOT, - "wm8996", codec); + irq_flags, "wm8996", codec); else ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq, - irq_flags | IRQF_ONESHOT, - "wm8996", codec); + irq_flags, "wm8996", codec); if (ret == 0) { /* Unmask the interrupt */ @@ -3100,7 +3098,6 @@ MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id); static struct i2c_driver wm8996_i2c_driver = { .driver = { .name = "wm8996", - .owner = THIS_MODULE, }, .probe = wm8996_i2c_probe, .remove = wm8996_i2c_remove, diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 4134dc7..b4dba3a 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -174,8 +174,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), -SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, @@ -187,8 +186,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), -SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, @@ -200,8 +198,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), -SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2), SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, @@ -213,8 +210,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, 24, 0, eq_tlv), -SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), -SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0), +ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2), SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, @@ -242,10 +238,10 @@ SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), -SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), -SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), -SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), -SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 8a8b1c0..ccb3b15 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -30,7 +30,7 @@ #include <sound/wm9081.h> #include "wm9081.h" -static struct reg_default wm9081_reg[] = { +static const struct reg_default wm9081_reg[] = { { 2, 0x00B9 }, /* R2 - Analogue Lineout */ { 3, 0x00B9 }, /* R3 - Analogue Speaker PGA */ { 4, 0x0001 }, /* R4 - VMID Control */ @@ -243,13 +243,12 @@ static int wm9081_reset(struct regmap *map) static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0); static const DECLARE_TLV_DB_SCALE(drc_out_tlv, -2250, 75, 0); static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0); -static unsigned int drc_max_tlv[] = { - TLV_DB_RANGE_HEAD(4), +static const DECLARE_TLV_DB_RANGE(drc_max_tlv, 0, 0, TLV_DB_SCALE_ITEM(1200, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), - 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0) +); static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -300, 50, 0); @@ -1378,7 +1377,6 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id); static struct i2c_driver wm9081_i2c_driver = { .driver = { .name = "wm9081", - .owner = THIS_MODULE, }, .probe = wm9081_i2c_probe, .remove = wm9081_i2c_remove, diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 13d23fc..5d73729 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -162,23 +162,20 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec) dev_err(codec->dev, "Timed out waiting for DC Servo\n"); } -static const unsigned int in_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static const DECLARE_TLV_DB_RANGE(in_tlv, 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0), 1, 3, TLV_DB_SCALE_ITEM(-350, 350, 0), - 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0), -}; -static const unsigned int mix_tlv[] = { - TLV_DB_RANGE_HEAD(2), + 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0) +); +static const DECLARE_TLV_DB_RANGE(mix_tlv, 0, 2, TLV_DB_SCALE_ITEM(-1200, 300, 0), - 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0) +); static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); -static const unsigned int spkboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(spkboost_tlv, 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), - 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0), -}; + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0) +); static const struct snd_kcontrol_new wm9090_controls[] = { SOC_SINGLE_TLV("IN1A Volume", WM9090_IN1_LINE_INPUT_A_VOLUME, 0, 6, 0, @@ -636,7 +633,6 @@ MODULE_DEVICE_TABLE(i2c, wm9090_id); static struct i2c_driver wm9090_i2c_driver = { .driver = { .name = "wm9090", - .owner = THIS_MODULE, }, .probe = wm9090_i2c_probe, .remove = wm9090_i2c_remove, diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 5cc457e..744842c 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -22,6 +22,9 @@ #include "wm9705.h" +#define WM9705_VENDOR_ID 0x574d4c05 +#define WM9705_VENDOR_ID_MASK 0xffffffff + /* * WM9705 register cache */ @@ -293,21 +296,6 @@ static struct snd_soc_dai_driver wm9705_dai[] = { } }; -static int wm9705_reset(struct snd_soc_codec *codec) -{ - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - - if (soc_ac97_ops->reset) { - soc_ac97_ops->reset(ac97); - if (ac97_read(codec, 0) == wm9705_reg[0]) - return 0; /* Success */ - } - - dev_err(codec->dev, "Failed to reset: AC97 link error\n"); - - return -EIO; -} - #ifdef CONFIG_PM static int wm9705_soc_suspend(struct snd_soc_codec *codec) { @@ -324,7 +312,8 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) int i, ret; u16 *cache = codec->reg_cache; - ret = wm9705_reset(codec); + ret = snd_ac97_reset(ac97, true, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); if (ret < 0) return ret; @@ -342,30 +331,17 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) static int wm9705_soc_probe(struct snd_soc_codec *codec) { struct snd_ac97 *ac97; - int ret = 0; - ac97 = snd_soc_alloc_ac97_codec(codec); + ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); if (IS_ERR(ac97)) { - ret = PTR_ERR(ac97); dev_err(codec->dev, "Failed to register AC97 codec\n"); - return ret; + return PTR_ERR(ac97); } - ret = wm9705_reset(codec); - if (ret) - goto err_put_device; - - ret = device_add(&ac97->dev); - if (ret) - goto err_put_device; - snd_soc_codec_set_drvdata(codec, ac97); return 0; - -err_put_device: - put_device(&ac97->dev); - return ret; } static int wm9705_soc_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1fda104..488a922 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -23,6 +23,9 @@ #include <sound/tlv.h> #include "wm9712.h" +#define WM9712_VENDOR_ID 0x574d4c12 +#define WM9712_VENDOR_ID_MASK 0xffffffff + struct wm9712_priv { struct snd_ac97 *ac97; unsigned int hp_mixer[2]; @@ -613,35 +616,14 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) -{ - struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - - if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(wm9712->ac97); - if (ac97_read(codec, 0) == wm9712_reg[0]) - return 1; - } - - soc_ac97_ops->reset(wm9712->ac97); - if (soc_ac97_ops->warm_reset) - soc_ac97_ops->warm_reset(wm9712->ac97); - if (ac97_read(codec, 0) != wm9712_reg[0]) - goto err; - return 0; - -err: - dev_err(codec->dev, "Failed to reset: AC97 link error\n"); - return -EIO; -} - static int wm9712_soc_resume(struct snd_soc_codec *codec) { struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); int i, ret; u16 *cache = codec->reg_cache; - ret = wm9712_reset(codec, 1); + ret = snd_ac97_reset(wm9712->ac97, true, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); if (ret < 0) return ret; @@ -663,31 +645,20 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) static int wm9712_soc_probe(struct snd_soc_codec *codec) { struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - int ret = 0; + int ret; - wm9712->ac97 = snd_soc_alloc_ac97_codec(codec); + wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); if (IS_ERR(wm9712->ac97)) { ret = PTR_ERR(wm9712->ac97); dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); return ret; } - ret = wm9712_reset(codec, 0); - if (ret < 0) - goto err_put_device; - - ret = device_add(&wm9712->ac97->dev); - if (ret) - goto err_put_device; - /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); return 0; - -err_put_device: - put_device(&wm9712->ac97->dev); - return ret; } static int wm9712_soc_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 89cd2d6..4083a51 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -29,6 +29,9 @@ #include "wm9713.h" +#define WM9713_VENDOR_ID 0x574d4c13 +#define WM9713_VENDOR_ID_MASK 0xffffffff + struct wm9713_priv { struct snd_ac97 *ac97; u32 pll_in; /* PLL input frequency */ @@ -116,11 +119,10 @@ SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */ static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0); -static unsigned int mic_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(mic_tlv, 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), - 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0), -}; + 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0) +); static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv), @@ -1123,28 +1125,6 @@ static struct snd_soc_dai_driver wm9713_dai[] = { }, }; -int wm9713_reset(struct snd_soc_codec *codec, int try_warm) -{ - struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - - if (try_warm && soc_ac97_ops->warm_reset) { - soc_ac97_ops->warm_reset(wm9713->ac97); - if (ac97_read(codec, 0) == wm9713_reg[0]) - return 1; - } - - soc_ac97_ops->reset(wm9713->ac97); - if (soc_ac97_ops->warm_reset) - soc_ac97_ops->warm_reset(wm9713->ac97); - if (ac97_read(codec, 0) != wm9713_reg[0]) { - dev_err(codec->dev, "Failed to reset: AC97 link error\n"); - return -EIO; - } - - return 0; -} -EXPORT_SYMBOL_GPL(wm9713_reset); - static int wm9713_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -1196,7 +1176,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) int i, ret; u16 *cache = codec->reg_cache; - ret = wm9713_reset(codec, 1); + ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID, + WM9713_VENDOR_ID_MASK); if (ret < 0) return ret; @@ -1222,32 +1203,18 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - int ret = 0, reg; + int reg; - wm9713->ac97 = snd_soc_alloc_ac97_codec(codec); + wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, + WM9713_VENDOR_ID_MASK); if (IS_ERR(wm9713->ac97)) return PTR_ERR(wm9713->ac97); - /* do a cold reset for the controller and then try - * a warm reset followed by an optional cold reset for codec */ - wm9713_reset(codec, 0); - ret = wm9713_reset(codec, 1); - if (ret < 0) - goto err_put_device; - - ret = device_add(&wm9713->ac97->dev); - if (ret) - goto err_put_device; - /* unmute the adc - move to kcontrol */ reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); return 0; - -err_put_device: - put_device(&wm9713->ac97->dev); - return ret; } static int wm9713_soc_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h index 793da86..53df11b 100644 --- a/sound/soc/codecs/wm9713.h +++ b/sound/soc/codecs/wm9713.h @@ -45,6 +45,4 @@ #define WM9713_DAI_AC97_AUX 1 #define WM9713_DAI_PCM_VOICE 2 -int wm9713_reset(struct snd_soc_codec *codec, int try_warm); - #endif diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index fd86bd1..624b3b9 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -38,11 +38,10 @@ static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1); static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0); -static const unsigned int spkboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const DECLARE_TLV_DB_RANGE(spkboost_tlv, 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), - 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0), -}; + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0) +); static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0); static const char *speaker_ref_text[] = { diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 56cb4d9..ec98548 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -651,23 +651,15 @@ static const struct snd_soc_component_driver davinci_i2s_component = { static int davinci_i2s_probe(struct platform_device *pdev) { struct davinci_mcbsp_dev *dev; - struct resource *mem, *ioarea, *res; + struct resource *mem, *res; + void __iomem *io_base; int *dma; int ret; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - - ioarea = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), - pdev->name); - if (!ioarea) { - dev_err(&pdev->dev, "McBSP region already claimed\n"); - return -EBUSY; - } + io_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev), GFP_KERNEL); @@ -679,12 +671,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) return -ENODEV; clk_enable(dev->clk); - dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!dev->base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err_release_clk; - } + dev->base = io_base; dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index b960e62..add6bb9 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1613,7 +1613,7 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) static int davinci_mcasp_probe(struct platform_device *pdev) { struct snd_dmaengine_dai_dma_data *dma_data; - struct resource *mem, *ioarea, *res, *dat; + struct resource *mem, *res, *dat; struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; char *irq_name; @@ -1648,22 +1648,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) } } - ioarea = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), pdev->name); - if (!ioarea) { - dev_err(&pdev->dev, "Audio region already claimed\n"); - return -EBUSY; - } + mcasp->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(mcasp->base)) + return PTR_ERR(mcasp->base); pm_runtime_enable(&pdev->dev); - mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!mcasp->base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err; - } - mcasp->op_mode = pdata->op_mode; /* sanity check for tdm slots parameter */ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index fabd05f..c77d921 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -231,8 +231,9 @@ static int davinci_vcif_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, davinci_vcif_dev); - ret = snd_soc_register_component(&pdev->dev, &davinci_vcif_component, - &davinci_vcif_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, + &davinci_vcif_component, + &davinci_vcif_dai, 1); if (ret != 0) { dev_err(&pdev->dev, "could not register dai\n"); return ret; @@ -241,23 +242,14 @@ static int davinci_vcif_probe(struct platform_device *pdev) ret = edma_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - snd_soc_unregister_component(&pdev->dev); return ret; } return 0; } -static int davinci_vcif_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - - return 0; -} - static struct platform_driver davinci_vcif_driver = { .probe = davinci_vcif_probe, - .remove = davinci_vcif_remove, .driver = { .name = "davinci-vcif", }, diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index e1aa3834..883087f 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -182,7 +182,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) ); } else { if (np) { - /* The eukrea,asoc-tlv320 driver was explicitely + /* The eukrea,asoc-tlv320 driver was explicitly * requested (through the device tree). */ dev_err(&pdev->dev, diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index de43887..5aeb6ed 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -23,6 +23,7 @@ #include "../codecs/sgtl5000.h" #include "../codecs/wm8962.h" +#include "../codecs/wm8960.h" #define RX 0 #define TX 1 @@ -407,6 +408,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct fsl_asoc_card_priv *priv; struct i2c_client *codec_dev; struct clk *codec_clk; + const char *codec_dai_name; u32 width; int ret; @@ -459,6 +461,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Diversify the card configurations */ if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { + codec_dai_name = "cs42888"; priv->card.set_bias_level = NULL; priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; @@ -467,14 +470,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.slot_width = 32; priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { + codec_dai_name = "sgtl5000"; priv->codec_priv.mclk_id = SGTL5000_SYSCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { + codec_dai_name = "wm8962"; priv->card.set_bias_level = fsl_asoc_card_set_bias_level; priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; priv->codec_priv.pll_id = WM8962_FLL; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { + codec_dai_name = "wm8960-hifi"; + priv->card.set_bias_level = fsl_asoc_card_set_bias_level; + priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; + priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); return -EINVAL; @@ -521,7 +532,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; priv->dai_link[0].codec_of_node = codec_np; - priv->dai_link[0].codec_dai_name = codec_dev->name; + priv->dai_link[0].codec_dai_name = codec_dai_name; priv->dai_link[0].platform_of_node = cpu_np; priv->dai_link[0].dai_fmt = priv->dai_fmt; priv->card.num_links = 1; @@ -530,7 +541,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* DPCM DAI Links only if ASRC exsits */ priv->dai_link[1].cpu_of_node = asrc_np; priv->dai_link[1].platform_of_node = asrc_np; - priv->dai_link[2].codec_dai_name = codec_dev->name; + priv->dai_link[2].codec_dai_name = codec_dai_name; priv->dai_link[2].codec_of_node = codec_np; priv->dai_link[2].cpu_of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; @@ -578,6 +589,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, + { .compatible = "fsl,imx-audio-wm8960", }, {} }; diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index c068494..9f087d4 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -931,14 +931,29 @@ static int fsl_asrc_probe(struct platform_device *pdev) static int fsl_asrc_runtime_resume(struct device *dev) { struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); - int i; + int i, ret; - clk_prepare_enable(asrc_priv->mem_clk); - clk_prepare_enable(asrc_priv->ipg_clk); - for (i = 0; i < ASRC_CLK_MAX_NUM; i++) - clk_prepare_enable(asrc_priv->asrck_clk[i]); + ret = clk_prepare_enable(asrc_priv->mem_clk); + if (ret) + return ret; + ret = clk_prepare_enable(asrc_priv->ipg_clk); + if (ret) + goto disable_mem_clk; + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { + ret = clk_prepare_enable(asrc_priv->asrck_clk[i]); + if (ret) + goto disable_asrck_clk; + } return 0; + +disable_asrck_clk: + for (i--; i >= 0; i--) + clk_disable_unprepare(asrc_priv->asrck_clk[i]); + clk_disable_unprepare(asrc_priv->ipg_clk); +disable_mem_clk: + clk_disable_unprepare(asrc_priv->mem_clk); + return ret; } static int fsl_asrc_runtime_suspend(struct device *dev) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 45eee13..837979e 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -839,7 +839,7 @@ static int fsl_esai_probe(struct platform_device *pdev) return ret; } - ret = imx_pcm_dma_init(pdev); + ret = imx_pcm_dma_init(pdev, IMX_ESAI_DMABUF_SIZE); if (ret) dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 5c73bea..a18fd92 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -791,7 +791,7 @@ static int fsl_sai_probe(struct platform_device *pdev) return ret; if (sai->sai_on_imx) - return imx_pcm_dma_init(pdev); + return imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE); else return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); } diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 0662809..b95fbc3 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -13,7 +13,8 @@ #define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S20_3LE |\ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) /* SAI Register Map Register */ #define FSL_SAI_TCSR 0x00 /* SAI Transmit Control */ @@ -45,7 +46,7 @@ #define FSL_SAI_xFR(tx) (tx ? FSL_SAI_TFR : FSL_SAI_RFR) #define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR) -/* SAI Transmit/Recieve Control Register */ +/* SAI Transmit/Receive Control Register */ #define FSL_SAI_CSR_TERE BIT(31) #define FSL_SAI_CSR_FR BIT(25) #define FSL_SAI_CSR_SR BIT(24) @@ -67,10 +68,10 @@ #define FSL_SAI_CSR_FRIE BIT(8) #define FSL_SAI_CSR_FRDE BIT(0) -/* SAI Transmit and Recieve Configuration 1 Register */ +/* SAI Transmit and Receive Configuration 1 Register */ #define FSL_SAI_CR1_RFW_MASK 0x1f -/* SAI Transmit and Recieve Configuration 2 Register */ +/* SAI Transmit and Receive Configuration 2 Register */ #define FSL_SAI_CR2_SYNC BIT(30) #define FSL_SAI_CR2_MSEL_MASK (0x3 << 26) #define FSL_SAI_CR2_MSEL_BUS 0 @@ -82,12 +83,12 @@ #define FSL_SAI_CR2_BCD_MSTR BIT(24) #define FSL_SAI_CR2_DIV_MASK 0xff -/* SAI Transmit and Recieve Configuration 3 Register */ +/* SAI Transmit and Receive Configuration 3 Register */ #define FSL_SAI_CR3_TRCE BIT(16) #define FSL_SAI_CR3_WDFL(x) (x) #define FSL_SAI_CR3_WDFL_MASK 0x1f -/* SAI Transmit and Recieve Configuration 4 Register */ +/* SAI Transmit and Receive Configuration 4 Register */ #define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) #define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) #define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) @@ -97,7 +98,7 @@ #define FSL_SAI_CR4_FSP BIT(1) #define FSL_SAI_CR4_FSD_MSTR BIT(0) -/* SAI Transmit and Recieve Configuration 5 Register */ +/* SAI Transmit and Receive Configuration 5 Register */ #define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24) #define FSL_SAI_CR5_WNW_MASK (0x1f << 24) #define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 8e93221..ab729f2 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -454,7 +454,8 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct platform_device *pdev = spdif_priv->pdev; struct regmap *regmap = spdif_priv->regmap; - u32 scr, mask, i; + u32 scr, mask; + int i; int ret; /* Reset module and interrupts only for first initialization */ @@ -482,13 +483,18 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXFIFO_FSEL_MASK; - for (i = 0; i < SPDIF_TXRATE_MAX; i++) - clk_prepare_enable(spdif_priv->txclk[i]); + for (i = 0; i < SPDIF_TXRATE_MAX; i++) { + ret = clk_prepare_enable(spdif_priv->txclk[i]); + if (ret) + goto disable_txclk; + } } else { scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC; mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; - clk_prepare_enable(spdif_priv->rxclk); + ret = clk_prepare_enable(spdif_priv->rxclk); + if (ret) + goto err; } regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); @@ -497,6 +503,9 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, return 0; +disable_txclk: + for (i--; i >= 0; i--) + clk_disable_unprepare(spdif_priv->txclk[i]); err: clk_disable_unprepare(spdif_priv->coreclk); @@ -707,7 +716,7 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, return ret; } -/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */ +/* Q-subcode information. The byte size is SPDIF_UBITS_SIZE/8 */ static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -739,7 +748,7 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, return ret; } -/* Valid bit infomation */ +/* Valid bit information */ static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -767,7 +776,7 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, return 0; } -/* DPLL lock infomation */ +/* DPLL lock information */ static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1255,7 +1264,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } - ret = imx_pcm_dma_init(pdev); + ret = imx_pcm_dma_init(pdev, IMX_SPDIF_DMABUF_SIZE); if (ret) dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c0b940e..8ec6fb2 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -156,7 +156,7 @@ struct fsl_ssi_soc_data { * * @dbg_stats: Debugging statistics * - * @soc: SoC specifc data + * @soc: SoC specific data */ struct fsl_ssi_private { struct regmap *regs; @@ -900,14 +900,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; break; default: - return -EINVAL; + if (!fsl_ssi_is_ac97(ssi_private)) + return -EINVAL; } stcr |= strcr; srcr |= strcr; - if (ssi_private->cpu_dai_drv.symmetric_rates) { - /* Need to clear RXDIR when using SYNC mode */ + if (ssi_private->cpu_dai_drv.symmetric_rates + || fsl_ssi_is_ac97(ssi_private)) { + /* Need to clear RXDIR when using SYNC or AC97 mode */ srcr &= ~CCSR_SSI_SRCR_RXDIR; scr |= CCSR_SSI_SCR_SYN; } @@ -1101,6 +1103,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = { static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .bus_control = true, + .probe = fsl_ssi_dai_probe, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -1127,10 +1130,17 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, struct regmap *regs = fsl_ac97_data->regs; unsigned int lreg; unsigned int lval; + int ret; if (reg > 0x7f) return; + ret = clk_prepare_enable(fsl_ac97_data->clk); + if (ret) { + pr_err("ac97 write clk_prepare_enable failed: %d\n", + ret); + return; + } lreg = reg << 12; regmap_write(regs, CCSR_SSI_SACADD, lreg); @@ -1141,6 +1151,8 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK, CCSR_SSI_SACNT_WR); udelay(100); + + clk_disable_unprepare(fsl_ac97_data->clk); } static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, @@ -1151,6 +1163,14 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, unsigned short val = -1; u32 reg_val; unsigned int lreg; + int ret; + + ret = clk_prepare_enable(fsl_ac97_data->clk); + if (ret) { + pr_err("ac97 read clk_prepare_enable failed: %d\n", + ret); + return -1; + } lreg = (reg & 0x7f) << 12; regmap_write(regs, CCSR_SSI_SACADD, lreg); @@ -1162,6 +1182,8 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, regmap_read(regs, CCSR_SSI_SACDAT, ®_val); val = (reg_val >> 4) & 0xffff; + clk_disable_unprepare(fsl_ac97_data->clk); + return val; } @@ -1210,7 +1232,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, } } - /* For those SLAVE implementations, we ingore non-baudclk cases + /* For those SLAVE implementations, we ignore non-baudclk cases * and, instead, abandon MASTER mode that needs baud clock. */ ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); @@ -1257,7 +1279,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, if (ret) goto error_pcm; } else { - ret = imx_pcm_dma_init(pdev); + ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); if (ret) goto error_pcm; } @@ -1320,7 +1342,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) fsl_ac97_data = ssi_private; - snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); + ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); + if (ret) { + dev_err(&pdev->dev, "could not set AC'97 ops\n"); + return ret; + } } else { /* Initialize this copy of the CPU DAI driver structure */ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, @@ -1357,7 +1383,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* Are the RX and the TX clocks locked? */ if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { - ssi_private->cpu_dai_drv.symmetric_rates = 1; + if (!fsl_ssi_is_ac97(ssi_private)) + ssi_private->cpu_dai_drv.symmetric_rates = 1; + ssi_private->cpu_dai_drv.symmetric_channels = 1; ssi_private->cpu_dai_drv.symmetric_samplebits = 1; } @@ -1434,6 +1462,27 @@ done: _fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private, ssi_private->dai_fmt); + if (fsl_ssi_is_ac97(ssi_private)) { + u32 ssi_idx; + + ret = of_property_read_u32(np, "cell-index", &ssi_idx); + if (ret) { + dev_err(&pdev->dev, "cannot get SSI index property\n"); + goto error_sound_card; + } + + ssi_private->pdev = + platform_device_register_data(NULL, + "ac97-codec", ssi_idx, NULL, 0); + if (IS_ERR(ssi_private->pdev)) { + ret = PTR_ERR(ssi_private->pdev); + dev_err(&pdev->dev, + "failed to register AC97 codec platform: %d\n", + ret); + goto error_sound_card; + } + } + return 0; error_sound_card: @@ -1458,6 +1507,9 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); + if (fsl_ssi_is_ac97(ssi_private)) + snd_soc_set_ac97_ops(NULL); + return 0; } diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 0db94f49..1fc01ed 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -40,7 +40,7 @@ static const struct snd_pcm_hardware imx_pcm_hardware = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, + .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE, .period_bytes_min = 128, .period_bytes_max = 65535, /* Limited by SDMA engine */ .periods_min = 2, @@ -52,13 +52,30 @@ static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = { .pcm_hardware = &imx_pcm_hardware, .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, .compat_filter_fn = filter, - .prealloc_buffer_size = IMX_SSI_DMABUF_SIZE, + .prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE, }; -int imx_pcm_dma_init(struct platform_device *pdev) +int imx_pcm_dma_init(struct platform_device *pdev, size_t size) { + struct snd_dmaengine_pcm_config *config; + struct snd_pcm_hardware *pcm_hardware; + + config = devm_kzalloc(&pdev->dev, + sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL); + *config = imx_dmaengine_pcm_config; + if (size) + config->prealloc_buffer_size = size; + + pcm_hardware = devm_kzalloc(&pdev->dev, + sizeof(struct snd_pcm_hardware), GFP_KERNEL); + *pcm_hardware = imx_pcm_hardware; + if (size) + pcm_hardware->buffer_bytes_max = size; + + config->pcm_hardware = pcm_hardware; + return devm_snd_dmaengine_pcm_register(&pdev->dev, - &imx_dmaengine_pcm_config, + config, SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(imx_pcm_dma_init); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index c79cb27..133c4470a 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -20,6 +20,11 @@ */ #define IMX_SSI_DMABUF_SIZE (64 * 1024) +#define IMX_DEFAULT_DMABUF_SIZE (64 * 1024) +#define IMX_SAI_DMABUF_SIZE (64 * 1024) +#define IMX_SPDIF_DMABUF_SIZE (64 * 1024) +#define IMX_ESAI_DMABUF_SIZE (256 * 1024) + static inline void imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data, int dma, enum sdma_peripheral_type peripheral_type) @@ -39,9 +44,9 @@ struct imx_pcm_fiq_params { }; #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) -int imx_pcm_dma_init(struct platform_device *pdev); +int imx_pcm_dma_init(struct platform_device *pdev, size_t size); #else -static inline int imx_pcm_dma_init(struct platform_device *pdev) +static inline int imx_pcm_dma_init(struct platform_device *pdev, size_t size) { return -ENODEV; } diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 461ce27..48b2d24 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -603,7 +603,7 @@ static int imx_ssi_probe(struct platform_device *pdev) ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params); - ssi->dma_init = imx_pcm_dma_init(pdev); + ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); if (ssi->fiq_init && ssi->dma_init) { ret = ssi->fiq_init; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index d555493..3ff76d4 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -76,6 +76,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = &priv->dai_props[rtd - rtd->card->rtd]; @@ -91,8 +92,16 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, mclk = params_rate(params) * mclk_fs; ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + goto err; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + goto err; } +err: return ret; } diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index f3060a4..05fde5e6e 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -26,14 +26,9 @@ config SND_SST_IPC_ACPI depends on ACPI config SND_SOC_INTEL_SST - tristate "ASoC support for Intel(R) Smart Sound Technology" + tristate select SND_SOC_INTEL_SST_ACPI if ACPI depends on (X86 || COMPILE_TEST) - depends on DW_DMAC_CORE - help - This adds support for Intel(R) Smart Sound Technology (SST). - Say Y if you have such a device - If unsure select "N". config SND_SOC_INTEL_SST_ACPI tristate @@ -46,8 +41,9 @@ config SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_HASWELL_MACH tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \ - I2C_DESIGNWARE_PLATFORM + depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM + depends on DW_DMAC_CORE + select SND_SOC_INTEL_SST select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 help @@ -58,7 +54,9 @@ config SND_SOC_INTEL_HASWELL_MACH config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C + depends on X86_INTEL_LPSS && I2C + depends on DW_DMAC_CORE + select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_RT5640 help @@ -67,7 +65,9 @@ config SND_SOC_INTEL_BYT_RT5640_MACH config SND_SOC_INTEL_BYT_MAX98090_MACH tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C + depends on X86_INTEL_LPSS && I2C + depends on DW_DMAC_CORE + select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_MAX98090 help @@ -76,8 +76,10 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \ + depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM + depends on DW_DMAC_CORE + select SND_SOC_INTEL_SST select SND_SOC_INTEL_HASWELL select SND_SOC_RT286 help @@ -132,3 +134,8 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with MAX98090 audio codec it also can support TI jack chip as aux device. If unsure select "N". + +config SND_SOC_INTEL_SKYLAKE + tristate + select SND_HDA_EXT_CORE + select SND_SOC_INTEL_SST diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 6de5d5c..2b45435 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/ +obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/ # Machine support obj-$(CONFIG_SND_SOC) += boards/ diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 31e9b9e..d55388e 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -132,7 +132,7 @@ static int sst_send_slot_map(struct sst_data *drv) sizeof(cmd.header) + cmd.header.length); } -int sst_slot_enum_info(struct snd_kcontrol *kcontrol, +static int sst_slot_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct sst_enum *e = (struct sst_enum *)kcontrol->private_value; @@ -1298,7 +1298,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) dev_dbg(dai->dev, "Stream name=%s\n", dai->playback_widget->name); w = dai->playback_widget; - list_for_each_entry(p, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; @@ -1317,7 +1317,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) dev_dbg(dai->dev, "Stream name=%s\n", dai->capture_widget->name); w = dai->capture_widget; - list_for_each_entry(p, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 641ebe6..683e501 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -33,7 +33,6 @@ struct sst_device *sst; static DEFINE_MUTEX(sst_lock); -extern struct snd_compr_ops sst_platform_compr_ops; int sst_register_dsp(struct sst_device *dev) { diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index 2409b23..cb32cc7 100644 --- a/sound/soc/intel/atom/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -25,6 +25,7 @@ #include "sst-atom-controls.h" extern struct sst_device *sst; +extern struct snd_compr_ops sst_platform_compr_ops; #define SST_MONO 1 #define SST_STEREO 2 diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 0e0e4d9..ce689c5 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -151,6 +151,7 @@ static int sst_power_control(struct device *dev, bool state) usage_count = GET_USAGE_COUNT(dev); dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); if (ret < 0) { + pm_runtime_put_sync(dev); dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret); return ret; } @@ -204,8 +205,10 @@ static int sst_cdev_open(struct device *dev, struct intel_sst_drv *ctx = dev_get_drvdata(dev); retval = pm_runtime_get_sync(ctx->dev); - if (retval < 0) + if (retval < 0) { + pm_runtime_put_sync(ctx->dev); return retval; + } str_id = sst_get_stream(ctx, str_params); if (str_id > 0) { @@ -672,8 +675,10 @@ static int sst_send_byte_stream(struct device *dev, if (NULL == bytes) return -EINVAL; ret_val = pm_runtime_get_sync(ctx->dev); - if (ret_val < 0) + if (ret_val < 0) { + pm_runtime_put_sync(ctx->dev); return ret_val; + } ret_val = sst_send_byte_stream_mrfld(ctx, bytes); sst_pm_runtime_put(ctx); diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 5a27861..3dc7358 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -352,10 +352,9 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, * copy from mailbox **/ if (msg_high.part.large) { - data = kzalloc(msg_low, GFP_KERNEL); + data = kmemdup((void *)msg->mailbox_data, msg_low, GFP_KERNEL); if (!data) return; - memcpy(data, (void *) msg->mailbox_data, msg_low); /* Copy command id so that we can use to put sst to reset */ dsp_hdr = (struct ipc_dsp_hdr *)data; cmd_id = dsp_hdr->cmd_id; diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c index 7ab8cc9..d9f81b8 100644 --- a/sound/soc/intel/boards/byt-max98090.c +++ b/sound/soc/intel/boards/byt-max98090.c @@ -126,6 +126,7 @@ static struct snd_soc_dai_link byt_max98090_dais[] = { static struct snd_soc_card byt_max98090_card = { .name = "byt-max98090", + .owner = THIS_MODULE, .dai_link = byt_max98090_dais, .num_links = ARRAY_SIZE(byt_max98090_dais), .dapm_widgets = byt_max98090_widgets, diff --git a/sound/soc/intel/boards/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c index ae89b9b9..de9788a 100644 --- a/sound/soc/intel/boards/byt-rt5640.c +++ b/sound/soc/intel/boards/byt-rt5640.c @@ -197,6 +197,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { static struct snd_soc_card byt_rt5640_card = { .name = "byt-rt5640", + .owner = THIS_MODULE, .dai_link = byt_rt5640_dais, .num_links = ARRAY_SIZE(byt_rt5640_dais), .dapm_widgets = byt_rt5640_widgets, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 7f55d59..c445312 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -185,6 +185,7 @@ static struct snd_soc_dai_link byt_dailink[] = { /* SoC card */ static struct snd_soc_card snd_soc_card_byt = { .name = "baytrailcraudio", + .owner = THIS_MODULE, .dai_link = byt_dailink, .num_links = ARRAY_SIZE(byt_dailink), .dapm_widgets = byt_dapm_widgets, diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 70f8321..49f4869 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -104,21 +104,17 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, static int cht_ti_jack_event(struct notifier_block *nb, unsigned long event, void *data) { - struct snd_soc_jack *jack = (struct snd_soc_jack *)data; - struct snd_soc_dai *codec_dai = jack->card->rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = &jack->card->dapm; if (event & SND_JACK_MICROPHONE) { - - snd_soc_dapm_force_enable_pin(&codec->dapm, "SHDN"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "SHDN"); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_sync(dapm); } else { - - snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS"); - snd_soc_dapm_disable_pin(&codec->dapm, "SHDN"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_disable_pin(dapm, "SHDN"); + snd_soc_dapm_sync(dapm); } return 0; @@ -279,6 +275,7 @@ static struct snd_soc_dai_link cht_dailink[] = { /* SoC card */ static struct snd_soc_card snd_soc_card_cht = { .name = "chtmax98090", + .owner = THIS_MODULE, .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), .aux_dev = &cht_max98090_headset_dev, diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index bdcaf46..7be8461 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -305,6 +305,7 @@ static struct snd_soc_dai_link cht_dailink[] = { /* SoC card */ static struct snd_soc_card snd_soc_card_chtrt5645 = { .name = "chtrt5645", + .owner = THIS_MODULE, .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), .dapm_widgets = cht_dapm_widgets, @@ -317,6 +318,7 @@ static struct snd_soc_card snd_soc_card_chtrt5645 = { static struct snd_soc_card snd_soc_card_chtrt5650 = { .name = "chtrt5650", + .owner = THIS_MODULE, .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), .dapm_widgets = cht_dapm_widgets, diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 2c9cc5b..23fe040 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -323,6 +323,7 @@ static int cht_resume_post(struct snd_soc_card *card) /* SoC card */ static struct snd_soc_card snd_soc_card_cht = { .name = "cherrytrailcraudio", + .owner = THIS_MODULE, .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), .dapm_widgets = cht_dapm_widgets, diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 396d545..cbd568e 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -22,6 +22,8 @@ #include <linux/interrupt.h> #include <linux/firmware.h> +#include "../skylake/skl-sst-dsp.h" + struct sst_mem_block; struct sst_module; struct sst_fw; @@ -258,6 +260,8 @@ struct sst_mem_block { */ struct sst_dsp { + /* Shared for all platforms */ + /* runtime */ struct sst_dsp_device *sst_dev; spinlock_t spinlock; /* IPC locking */ @@ -268,10 +272,6 @@ struct sst_dsp { int irq; u32 id; - /* list of free and used ADSP memory blocks */ - struct list_head used_block_list; - struct list_head free_block_list; - /* operations */ struct sst_ops *ops; @@ -284,6 +284,12 @@ struct sst_dsp { /* mailbox */ struct sst_mailbox mailbox; + /* HSW/Byt data */ + + /* list of free and used ADSP memory blocks */ + struct list_head used_block_list; + struct list_head free_block_list; + /* SST FW files loaded and their modules */ struct list_head module_list; struct list_head fw_list; @@ -299,6 +305,15 @@ struct sst_dsp { /* DMA FW loading */ struct sst_dma *dma; bool fw_use_dma; + + /* SKL data */ + + /* To allocate CL dma buffers */ + struct skl_dsp_loader_ops dsp_ops; + struct skl_dsp_fw_ops fw_ops; + int sst_state; + struct skl_cl_dev cl_dev; + u32 intr_status; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index 64e9421..a627236 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/delay.h> #include "sst-dsp.h" #include "sst-dsp-priv.h" @@ -196,6 +197,22 @@ int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, } EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); +/* This is for registers bits with attribute RWC */ +void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value) +{ + unsigned int old, new; + u32 ret; + + ret = sst_dsp_shim_read_unlocked(sst, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + sst_dsp_shim_write_unlocked(sst, offset, new); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced_unlocked); + int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, u32 mask, u32 value) { @@ -222,6 +239,60 @@ int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, } EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); +/* This is for registers bits with attribute RWC */ +void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + sst_dsp_shim_update_bits_forced_unlocked(sst, offset, mask, value); + spin_unlock_irqrestore(&sst->spinlock, flags); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced); + +int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, + u32 target, u32 timeout, char *operation) +{ + int time, ret; + u32 reg; + bool done = false; + + /* + * we will poll for couple of ms using mdelay, if not successful + * then go to longer sleep using usleep_range + */ + + /* check if set state successful */ + for (time = 0; time < 5; time++) { + if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) { + done = true; + break; + } + mdelay(1); + } + + if (done == false) { + /* sleeping in 10ms steps so adjust timeout value */ + timeout /= 10; + + for (time = 0; time < timeout; time++) { + if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) + break; + + usleep_range(5000, 10000); + } + } + + reg = sst_dsp_shim_read_unlocked(ctx, offset); + dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation, + (time < timeout) ? "successful" : "timedout"); + ret = time < timeout ? 0 : -ETIME; + + return ret; +} +EXPORT_SYMBOL_GPL(sst_dsp_register_poll); + void sst_dsp_dump(struct sst_dsp *sst) { if (sst->ops->dump) diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 96aeb25..1f45f18 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -230,6 +230,8 @@ void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value); u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset); int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, u64 mask, u64 value); +void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value); /* SHIM Read / Write Unlocked for callers already holding sst lock */ void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value); @@ -240,6 +242,8 @@ void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value); u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset); int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, u64 mask, u64 value); +void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value); /* Internal generic low-level SST IO functions - can be overidden */ void sst_shim32_write(void __iomem *addr, u32 offset, u32 value); @@ -278,6 +282,8 @@ void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes); void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes); void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes); void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes); +int sst_dsp_register_poll(struct sst_dsp *dsp, u32 offset, u32 mask, + u32 expected_value, u32 timeout, char *operation); /* Debug */ void sst_dsp_dump(struct sst_dsp *sst); diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile new file mode 100644 index 0000000..27db221 --- /dev/null +++ b/sound/soc/intel/skylake/Makefile @@ -0,0 +1,9 @@ +snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o + +obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o + +# Skylake IPC Support +snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \ + skl-sst.o + +obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c new file mode 100644 index 0000000..826d4fd --- /dev/null +++ b/sound/soc/intel/skylake/skl-messages.c @@ -0,0 +1,884 @@ +/* + * skl-message.c - HDA DSP interface for FW registration, Pipe and Module + * configurations + * + * Copyright (C) 2015 Intel Corp + * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> + * Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/slab.h> +#include <linux/pci.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" +#include "skl.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-topology.h" +#include "skl-tplg-interface.h" + +static int skl_alloc_dma_buf(struct device *dev, + struct snd_dma_buffer *dmab, size_t size) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + if (!bus) + return -ENODEV; + + return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, size, dmab); +} + +static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + if (!bus) + return -ENODEV; + + bus->io_ops->dma_free_pages(bus, dmab); + + return 0; +} + +int skl_init_dsp(struct skl *skl) +{ + void __iomem *mmio_base; + struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = ebus_to_hbus(ebus); + int irq = bus->irq; + struct skl_dsp_loader_ops loader_ops; + int ret; + + loader_ops.alloc_dma_buf = skl_alloc_dma_buf; + loader_ops.free_dma_buf = skl_free_dma_buf; + + /* enable ppcap interrupt */ + snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); + snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + + /* read the BAR of the ADSP MMIO */ + mmio_base = pci_ioremap_bar(skl->pci, 4); + if (mmio_base == NULL) { + dev_err(bus->dev, "ioremap error\n"); + return -ENXIO; + } + + ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, + loader_ops, &skl->skl_sst); + + dev_dbg(bus->dev, "dsp registration status=%d\n", ret); + + return ret; +} + +void skl_free_dsp(struct skl *skl) +{ + struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl_sst *ctx = skl->skl_sst; + + /* disable ppcap interrupt */ + snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); + + skl_sst_dsp_cleanup(bus->dev, ctx); + if (ctx->dsp->addr.lpe) + iounmap(ctx->dsp->addr.lpe); +} + +int skl_suspend_dsp(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + int ret; + + /* if ppcap is not supported return 0 */ + if (!skl->ebus.ppcap) + return 0; + + ret = skl_dsp_sleep(ctx->dsp); + if (ret < 0) + return ret; + + /* disable ppcap interrupt */ + snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); + snd_hdac_ext_bus_ppcap_enable(&skl->ebus, false); + + return 0; +} + +int skl_resume_dsp(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + + /* if ppcap is not supported return 0 */ + if (!skl->ebus.ppcap) + return 0; + + /* enable ppcap interrupt */ + snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); + snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + + return skl_dsp_wake(ctx->dsp); +} + +enum skl_bitdepth skl_get_bit_depth(int params) +{ + switch (params) { + case 8: + return SKL_DEPTH_8BIT; + + case 16: + return SKL_DEPTH_16BIT; + + case 24: + return SKL_DEPTH_24BIT; + + case 32: + return SKL_DEPTH_32BIT; + + default: + return SKL_DEPTH_INVALID; + + } +} + +static u32 skl_create_channel_map(enum skl_ch_cfg ch_cfg) +{ + u32 config; + + switch (ch_cfg) { + case SKL_CH_CFG_MONO: + config = (0xFFFFFFF0 | SKL_CHANNEL_LEFT); + break; + + case SKL_CH_CFG_STEREO: + config = (0xFFFFFF00 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_RIGHT << 4)); + break; + + case SKL_CH_CFG_2_1: + config = (0xFFFFF000 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_RIGHT << 4) + | (SKL_CHANNEL_LFE << 8)); + break; + + case SKL_CH_CFG_3_0: + config = (0xFFFFF000 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_CENTER << 4) + | (SKL_CHANNEL_RIGHT << 8)); + break; + + case SKL_CH_CFG_3_1: + config = (0xFFFF0000 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_CENTER << 4) + | (SKL_CHANNEL_RIGHT << 8) + | (SKL_CHANNEL_LFE << 12)); + break; + + case SKL_CH_CFG_QUATRO: + config = (0xFFFF0000 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_RIGHT << 4) + | (SKL_CHANNEL_LEFT_SURROUND << 8) + | (SKL_CHANNEL_RIGHT_SURROUND << 12)); + break; + + case SKL_CH_CFG_4_0: + config = (0xFFFF0000 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_CENTER << 4) + | (SKL_CHANNEL_RIGHT << 8) + | (SKL_CHANNEL_CENTER_SURROUND << 12)); + break; + + case SKL_CH_CFG_5_0: + config = (0xFFF00000 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_CENTER << 4) + | (SKL_CHANNEL_RIGHT << 8) + | (SKL_CHANNEL_LEFT_SURROUND << 12) + | (SKL_CHANNEL_RIGHT_SURROUND << 16)); + break; + + case SKL_CH_CFG_5_1: + config = (0xFF000000 | SKL_CHANNEL_CENTER + | (SKL_CHANNEL_LEFT << 4) + | (SKL_CHANNEL_RIGHT << 8) + | (SKL_CHANNEL_LEFT_SURROUND << 12) + | (SKL_CHANNEL_RIGHT_SURROUND << 16) + | (SKL_CHANNEL_LFE << 20)); + break; + + case SKL_CH_CFG_DUAL_MONO: + config = (0xFFFFFF00 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_LEFT << 4)); + break; + + case SKL_CH_CFG_I2S_DUAL_STEREO_0: + config = (0xFFFFFF00 | SKL_CHANNEL_LEFT + | (SKL_CHANNEL_RIGHT << 4)); + break; + + case SKL_CH_CFG_I2S_DUAL_STEREO_1: + config = (0xFFFF00FF | (SKL_CHANNEL_LEFT << 8) + | (SKL_CHANNEL_RIGHT << 12)); + break; + + default: + config = 0xFFFFFFFF; + break; + + } + + return config; +} + +/* + * Each module in DSP expects a base module configuration, which consists of + * PCM format information, which we calculate in driver and resource values + * which are read from widget information passed through topology binary + * This is send when we create a module with INIT_INSTANCE IPC msg + */ +static void skl_set_base_module_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_base_cfg *base_cfg) +{ + struct skl_module_fmt *format = &mconfig->in_fmt; + + base_cfg->audio_fmt.number_of_channels = (u8)format->channels; + + base_cfg->audio_fmt.s_freq = format->s_freq; + base_cfg->audio_fmt.bit_depth = format->bit_depth; + base_cfg->audio_fmt.valid_bit_depth = format->valid_bit_depth; + base_cfg->audio_fmt.ch_cfg = format->ch_cfg; + + dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x\n", + format->bit_depth, format->valid_bit_depth, + format->ch_cfg); + + base_cfg->audio_fmt.channel_map = skl_create_channel_map( + base_cfg->audio_fmt.ch_cfg); + + base_cfg->audio_fmt.interleaving = SKL_INTERLEAVING_PER_CHANNEL; + + base_cfg->cps = mconfig->mcps; + base_cfg->ibs = mconfig->ibs; + base_cfg->obs = mconfig->obs; +} + +/* + * Copies copier capabilities into copier module and updates copier module + * config size. + */ +static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, + struct skl_cpr_cfg *cpr_mconfig) +{ + if (mconfig->formats_config.caps_size == 0) + return; + + memcpy(cpr_mconfig->gtw_cfg.config_data, + mconfig->formats_config.caps, + mconfig->formats_config.caps_size); + + cpr_mconfig->gtw_cfg.config_length = + (mconfig->formats_config.caps_size) / 4; +} + +/* + * Calculate the gatewat settings required for copier module, type of + * gateway and index of gateway to use + */ +static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_cpr_cfg *cpr_mconfig) +{ + union skl_connector_node_id node_id = {0}; + struct skl_pipe_params *params = mconfig->pipe->p_params; + + switch (mconfig->dev_type) { + case SKL_DEVICE_BT: + node_id.node.dma_type = + (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? + SKL_DMA_I2S_LINK_OUTPUT_CLASS : + SKL_DMA_I2S_LINK_INPUT_CLASS; + node_id.node.vindex = params->host_dma_id + + (mconfig->vbus_id << 3); + break; + + case SKL_DEVICE_I2S: + node_id.node.dma_type = + (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? + SKL_DMA_I2S_LINK_OUTPUT_CLASS : + SKL_DMA_I2S_LINK_INPUT_CLASS; + node_id.node.vindex = params->host_dma_id + + (mconfig->time_slot << 1) + + (mconfig->vbus_id << 3); + break; + + case SKL_DEVICE_DMIC: + node_id.node.dma_type = SKL_DMA_DMIC_LINK_INPUT_CLASS; + node_id.node.vindex = mconfig->vbus_id + + (mconfig->time_slot); + break; + + case SKL_DEVICE_HDALINK: + node_id.node.dma_type = + (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? + SKL_DMA_HDA_LINK_OUTPUT_CLASS : + SKL_DMA_HDA_LINK_INPUT_CLASS; + node_id.node.vindex = params->link_dma_id; + break; + + default: + node_id.node.dma_type = + (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? + SKL_DMA_HDA_HOST_OUTPUT_CLASS : + SKL_DMA_HDA_HOST_INPUT_CLASS; + node_id.node.vindex = params->host_dma_id; + break; + } + + cpr_mconfig->gtw_cfg.node_id = node_id.val; + + if (SKL_CONN_SOURCE == mconfig->hw_conn_type) + cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs; + else + cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->ibs; + + cpr_mconfig->cpr_feature_mask = 0; + cpr_mconfig->gtw_cfg.config_length = 0; + + skl_copy_copier_caps(mconfig, cpr_mconfig); +} + +static void skl_setup_out_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_audio_data_format *out_fmt) +{ + struct skl_module_fmt *format = &mconfig->out_fmt; + + out_fmt->number_of_channels = (u8)format->channels; + out_fmt->s_freq = format->s_freq; + out_fmt->bit_depth = format->bit_depth; + out_fmt->valid_bit_depth = format->valid_bit_depth; + out_fmt->ch_cfg = format->ch_cfg; + + out_fmt->channel_map = skl_create_channel_map(out_fmt->ch_cfg); + out_fmt->interleaving = SKL_INTERLEAVING_PER_CHANNEL; + + dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n", + out_fmt->number_of_channels, format->s_freq, format->bit_depth); +} + +/* + * DSP needs SRC module for frequency conversion, SRC takes base module + * configuration and the target frequency as extra parameter passed as src + * config + */ +static void skl_set_src_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_src_module_cfg *src_mconfig) +{ + struct skl_module_fmt *fmt = &mconfig->out_fmt; + + skl_set_base_module_format(ctx, mconfig, + (struct skl_base_cfg *)src_mconfig); + + src_mconfig->src_cfg = fmt->s_freq; +} + +/* + * DSP needs updown module to do channel conversion. updown module take base + * module configuration and channel configuration + * It also take coefficients and now we have defaults applied here + */ +static void skl_set_updown_mixer_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_up_down_mixer_cfg *mixer_mconfig) +{ + struct skl_module_fmt *fmt = &mconfig->out_fmt; + int i = 0; + + skl_set_base_module_format(ctx, mconfig, + (struct skl_base_cfg *)mixer_mconfig); + mixer_mconfig->out_ch_cfg = fmt->ch_cfg; + + /* Select F/W default coefficient */ + mixer_mconfig->coeff_sel = 0x0; + + /* User coeff, don't care since we are selecting F/W defaults */ + for (i = 0; i < UP_DOWN_MIXER_MAX_COEFF; i++) + mixer_mconfig->coeff[i] = 0xDEADBEEF; +} + +/* + * 'copier' is DSP internal module which copies data from Host DMA (HDA host + * dma) or link (hda link, SSP, PDM) + * Here we calculate the copier module parameters, like PCM format, output + * format, gateway settings + * copier_module_config is sent as input buffer with INIT_INSTANCE IPC msg + */ +static void skl_set_copier_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_cpr_cfg *cpr_mconfig) +{ + struct skl_audio_data_format *out_fmt = &cpr_mconfig->out_fmt; + struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)cpr_mconfig; + + skl_set_base_module_format(ctx, mconfig, base_cfg); + + skl_setup_out_format(ctx, mconfig, out_fmt); + skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig); +} + +static u16 skl_get_module_param_size(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) +{ + u16 param_size; + + switch (mconfig->m_type) { + case SKL_MODULE_TYPE_COPIER: + param_size = sizeof(struct skl_cpr_cfg); + param_size += mconfig->formats_config.caps_size; + return param_size; + + case SKL_MODULE_TYPE_SRCINT: + return sizeof(struct skl_src_module_cfg); + + case SKL_MODULE_TYPE_UPDWMIX: + return sizeof(struct skl_up_down_mixer_cfg); + + default: + /* + * return only base cfg when no specific module type is + * specified + */ + return sizeof(struct skl_base_cfg); + } + + return 0; +} + +/* + * DSP firmware supports various modules like copier, SRC, updown etc. + * These modules required various parameters to be calculated and sent for + * the module initialization to DSP. By default a generic module needs only + * base module format configuration + */ + +static int skl_set_module_format(struct skl_sst *ctx, + struct skl_module_cfg *module_config, + u16 *module_config_size, + void **param_data) +{ + u16 param_size; + + param_size = skl_get_module_param_size(ctx, module_config); + + *param_data = kzalloc(param_size, GFP_KERNEL); + if (NULL == *param_data) + return -ENOMEM; + + *module_config_size = param_size; + + switch (module_config->m_type) { + case SKL_MODULE_TYPE_COPIER: + skl_set_copier_format(ctx, module_config, *param_data); + break; + + case SKL_MODULE_TYPE_SRCINT: + skl_set_src_format(ctx, module_config, *param_data); + break; + + case SKL_MODULE_TYPE_UPDWMIX: + skl_set_updown_mixer_format(ctx, module_config, *param_data); + break; + + default: + skl_set_base_module_format(ctx, module_config, *param_data); + break; + + } + + dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n", + module_config->id.module_id, param_size); + print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4, + *param_data, param_size, false); + return 0; +} + +static int skl_get_queue_index(struct skl_module_pin *mpin, + struct skl_module_inst_id id, int max) +{ + int i; + + for (i = 0; i < max; i++) { + if (mpin[i].id.module_id == id.module_id && + mpin[i].id.instance_id == id.instance_id) + return i; + } + + return -EINVAL; +} + +/* + * Allocates queue for each module. + * if dynamic, the pin_index is allocated 0 to max_pin. + * In static, the pin_index is fixed based on module_id and instance id + */ +static int skl_alloc_queue(struct skl_module_pin *mpin, + struct skl_module_inst_id id, int max) +{ + int i; + + /* + * if pin in dynamic, find first free pin + * otherwise find match module and instance id pin as topology will + * ensure a unique pin is assigned to this so no need to + * allocate/free + */ + for (i = 0; i < max; i++) { + if (mpin[i].is_dynamic) { + if (!mpin[i].in_use) { + mpin[i].in_use = true; + mpin[i].id.module_id = id.module_id; + mpin[i].id.instance_id = id.instance_id; + return i; + } + } else { + if (mpin[i].id.module_id == id.module_id && + mpin[i].id.instance_id == id.instance_id) + return i; + } + } + + return -EINVAL; +} + +static void skl_free_queue(struct skl_module_pin *mpin, int q_index) +{ + if (mpin[q_index].is_dynamic) { + mpin[q_index].in_use = false; + mpin[q_index].id.module_id = 0; + mpin[q_index].id.instance_id = 0; + } +} + +/* + * A module needs to be instanataited in DSP. A mdoule is present in a + * collection of module referred as a PIPE. + * We first calculate the module format, based on module type and then + * invoke the DSP by sending IPC INIT_INSTANCE using ipc helper + */ +int skl_init_module(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, char *param) +{ + u16 module_config_size = 0; + void *param_data = NULL; + int ret; + struct skl_ipc_init_instance_msg msg; + + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + + if (mconfig->pipe->state != SKL_PIPE_CREATED) { + dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n", + mconfig->pipe->state, mconfig->pipe->ppl_id); + return -EIO; + } + + ret = skl_set_module_format(ctx, mconfig, + &module_config_size, ¶m_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set module format ret=%d\n", ret); + return ret; + } + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.ppl_instance_id = mconfig->pipe->ppl_id; + msg.param_data_size = module_config_size; + msg.core_id = mconfig->core_id; + + ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to init instance ret=%d\n", ret); + kfree(param_data); + return ret; + } + mconfig->m_state = SKL_MODULE_INIT_DONE; + + return ret; +} + +static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg + *src_module, struct skl_module_cfg *dst_module) +{ + dev_dbg(ctx->dev, "%s: src module_id = %d src_instance=%d\n", + __func__, src_module->id.module_id, src_module->id.instance_id); + dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__, + dst_module->id.module_id, dst_module->id.instance_id); + + dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n", + src_module->m_state, dst_module->m_state); +} + +/* + * On module freeup, we need to unbind the module with modules + * it is already bind. + * Find the pin allocated and unbind then using bind_unbind IPC + */ +int skl_unbind_modules(struct skl_sst *ctx, + struct skl_module_cfg *src_mcfg, + struct skl_module_cfg *dst_mcfg) +{ + int ret; + struct skl_ipc_bind_unbind_msg msg; + struct skl_module_inst_id src_id = src_mcfg->id; + struct skl_module_inst_id dst_id = dst_mcfg->id; + int in_max = dst_mcfg->max_in_queue; + int out_max = src_mcfg->max_out_queue; + int src_index, dst_index; + + skl_dump_bind_info(ctx, src_mcfg, dst_mcfg); + + if (src_mcfg->m_state != SKL_MODULE_BIND_DONE) + return 0; + + /* + * if intra module unbind, check if both modules are BIND, + * then send unbind + */ + if ((src_mcfg->pipe->ppl_id != dst_mcfg->pipe->ppl_id) && + dst_mcfg->m_state != SKL_MODULE_BIND_DONE) + return 0; + else if (src_mcfg->m_state < SKL_MODULE_INIT_DONE && + dst_mcfg->m_state < SKL_MODULE_INIT_DONE) + return 0; + + /* get src queue index */ + src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max); + if (src_index < 0) + return -EINVAL; + + msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index; + + /* get dst queue index */ + dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max); + if (dst_index < 0) + return -EINVAL; + + msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index; + + msg.module_id = src_mcfg->id.module_id; + msg.instance_id = src_mcfg->id.instance_id; + msg.dst_module_id = dst_mcfg->id.module_id; + msg.dst_instance_id = dst_mcfg->id.instance_id; + msg.bind = false; + + ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); + if (!ret) { + src_mcfg->m_state = SKL_MODULE_UNINIT; + /* free queue only if unbind is success */ + skl_free_queue(src_mcfg->m_out_pin, src_index); + skl_free_queue(dst_mcfg->m_in_pin, dst_index); + } + + return ret; +} + +/* + * Once a module is instantiated it need to be 'bind' with other modules in + * the pipeline. For binding we need to find the module pins which are bind + * together + * This function finds the pins and then sends bund_unbind IPC message to + * DSP using IPC helper + */ +int skl_bind_modules(struct skl_sst *ctx, + struct skl_module_cfg *src_mcfg, + struct skl_module_cfg *dst_mcfg) +{ + int ret; + struct skl_ipc_bind_unbind_msg msg; + struct skl_module_inst_id src_id = src_mcfg->id; + struct skl_module_inst_id dst_id = dst_mcfg->id; + int in_max = dst_mcfg->max_in_queue; + int out_max = src_mcfg->max_out_queue; + int src_index, dst_index; + + skl_dump_bind_info(ctx, src_mcfg, dst_mcfg); + + if (src_mcfg->m_state < SKL_MODULE_INIT_DONE && + dst_mcfg->m_state < SKL_MODULE_INIT_DONE) + return 0; + + src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_id, out_max); + if (src_index < 0) + return -EINVAL; + + msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index; + dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_id, in_max); + if (dst_index < 0) { + skl_free_queue(src_mcfg->m_out_pin, src_index); + return -EINVAL; + } + + msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index; + + dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n", + msg.src_queue, msg.dst_queue); + + msg.module_id = src_mcfg->id.module_id; + msg.instance_id = src_mcfg->id.instance_id; + msg.dst_module_id = dst_mcfg->id.module_id; + msg.dst_instance_id = dst_mcfg->id.instance_id; + msg.bind = true; + + ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); + + if (!ret) { + src_mcfg->m_state = SKL_MODULE_BIND_DONE; + } else { + /* error case , if IPC fails, clear the queue index */ + skl_free_queue(src_mcfg->m_out_pin, src_index); + skl_free_queue(dst_mcfg->m_in_pin, dst_index); + } + + return ret; +} + +static int skl_set_pipe_state(struct skl_sst *ctx, struct skl_pipe *pipe, + enum skl_ipc_pipeline_state state) +{ + dev_dbg(ctx->dev, "%s: pipe_satate = %d\n", __func__, state); + + return skl_ipc_set_pipeline_state(&ctx->ipc, pipe->ppl_id, state); +} + +/* + * A pipeline is a collection of modules. Before a module in instantiated a + * pipeline needs to be created for it. + * This function creates pipeline, by sending create pipeline IPC messages + * to FW + */ +int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) +{ + int ret; + + dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id); + + ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages, + pipe->pipe_priority, pipe->ppl_id); + if (ret < 0) { + dev_err(ctx->dev, "Failed to create pipeline\n"); + return ret; + } + + pipe->state = SKL_PIPE_CREATED; + + return 0; +} + +/* + * A pipeline needs to be deleted on cleanup. If a pipeline is running, then + * pause the pipeline first and then delete it + * The pipe delete is done by sending delete pipeline IPC. DSP will stop the + * DMA engines and releases resources + */ +int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) +{ + int ret; + + dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); + + /* If pipe is not started, do not try to stop the pipe in FW. */ + if (pipe->state > SKL_PIPE_STARTED) { + ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED); + if (ret < 0) { + dev_err(ctx->dev, "Failed to stop pipeline\n"); + return ret; + } + + pipe->state = SKL_PIPE_PAUSED; + } else { + /* If pipe was not created in FW, do not try to delete it */ + if (pipe->state < SKL_PIPE_CREATED) + return 0; + + ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id); + if (ret < 0) + dev_err(ctx->dev, "Failed to delete pipeline\n"); + } + + return ret; +} + +/* + * A pipeline is also a scheduling entity in DSP which can be run, stopped + * For processing data the pipe need to be run by sending IPC set pipe state + * to DSP + */ +int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) +{ + int ret; + + dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); + + /* If pipe was not created in FW, do not try to pause or delete */ + if (pipe->state < SKL_PIPE_CREATED) + return 0; + + /* Pipe has to be paused before it is started */ + ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED); + if (ret < 0) { + dev_err(ctx->dev, "Failed to pause pipe\n"); + return ret; + } + + pipe->state = SKL_PIPE_PAUSED; + + ret = skl_set_pipe_state(ctx, pipe, PPL_RUNNING); + if (ret < 0) { + dev_err(ctx->dev, "Failed to start pipe\n"); + return ret; + } + + pipe->state = SKL_PIPE_STARTED; + + return 0; +} + +/* + * Stop the pipeline by sending set pipe state IPC + * DSP doesnt implement stop so we always send pause message + */ +int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) +{ + int ret; + + dev_dbg(ctx->dev, "In %s pipe=%d\n", __func__, pipe->ppl_id); + + /* If pipe was not created in FW, do not try to pause or delete */ + if (pipe->state < SKL_PIPE_PAUSED) + return 0; + + ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED); + if (ret < 0) { + dev_dbg(ctx->dev, "Failed to stop pipe\n"); + return ret; + } + + pipe->state = SKL_PIPE_CREATED; + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c new file mode 100644 index 0000000..13036b1 --- /dev/null +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -0,0 +1,140 @@ +/* + * skl-nhlt.c - Intel SKL Platform NHLT parsing + * + * Copyright (C) 2015 Intel Corp + * Author: Sanjiv Kumar <sanjiv.kumar@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include "skl.h" + +/* Unique identification for getting NHLT blobs */ +static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, + 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53}; + +#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" + +void __iomem *skl_nhlt_init(struct device *dev) +{ + acpi_handle handle; + union acpi_object *obj; + struct nhlt_resource_desc *nhlt_ptr = NULL; + + if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) { + dev_err(dev, "Requested NHLT device not found\n"); + return NULL; + } + + obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL); + if (obj && obj->type == ACPI_TYPE_BUFFER) { + nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; + + return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length); + } + + dev_err(dev, "device specific method to extract NHLT blob failed\n"); + return NULL; +} + +void skl_nhlt_free(void __iomem *addr) +{ + iounmap(addr); + addr = NULL; +} + +static struct nhlt_specific_cfg *skl_get_specific_cfg( + struct device *dev, struct nhlt_fmt *fmt, + u8 no_ch, u32 rate, u16 bps) +{ + struct nhlt_specific_cfg *sp_config; + struct wav_fmt *wfmt; + struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; + int i; + + dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); + + for (i = 0; i < fmt->fmt_count; i++) { + wfmt = &fmt_config->fmt_ext.fmt; + dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, + wfmt->bits_per_sample, wfmt->samples_per_sec); + if (wfmt->channels == no_ch && wfmt->samples_per_sec == rate && + wfmt->bits_per_sample == bps) { + sp_config = &fmt_config->config; + + return sp_config; + } + + fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + + fmt_config->config.size); + } + + return NULL; +} + +static void dump_config(struct device *dev, u32 instance_id, u8 linktype, + u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) +{ + dev_dbg(dev, "Input configuration\n"); + dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); + dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); + dev_dbg(dev, "bits_per_sample=%d\n", bps); +} + +static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, + u32 instance_id, u8 link_type, u8 dirn) +{ + dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d\n", + epnt->virtual_bus_id, epnt->linktype, epnt->direction); + + if ((epnt->virtual_bus_id == instance_id) && + (epnt->linktype == link_type) && + (epnt->direction == dirn)) + return true; + else + return false; +} + +struct nhlt_specific_cfg +*skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, + u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn) +{ + struct nhlt_fmt *fmt; + struct nhlt_endpoint *epnt; + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct device *dev = bus->dev; + struct nhlt_specific_cfg *sp_config; + struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + u16 bps = num_ch * s_fmt; + u8 j; + + dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); + + epnt = (struct nhlt_endpoint *)nhlt->desc; + + dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); + + for (j = 0; j < nhlt->endpoint_count; j++) { + if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) { + fmt = (struct nhlt_fmt *)(epnt->config.caps + + epnt->config.size); + sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps); + if (sp_config) + return sp_config; + } + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return NULL; +} diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h new file mode 100644 index 0000000..3769f9f --- /dev/null +++ b/sound/soc/intel/skylake/skl-nhlt.h @@ -0,0 +1,106 @@ +/* + * skl-nhlt.h - Intel HDA Platform NHLT header + * + * Copyright (C) 2015 Intel Corp + * Author: Sanjiv Kumar <sanjiv.kumar@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef __SKL_NHLT_H__ +#define __SKL_NHLT_H__ + +#include <linux/acpi.h> + +struct wav_fmt { + u16 fmt_tag; + u16 channels; + u32 samples_per_sec; + u32 avg_bytes_per_sec; + u16 block_align; + u16 bits_per_sample; + u16 cb_size; +} __packed; + +struct wav_fmt_ext { + struct wav_fmt fmt; + union samples { + u16 valid_bits_per_sample; + u16 samples_per_block; + u16 reserved; + } sample; + u32 channel_mask; + u8 sub_fmt[16]; +} __packed; + +enum nhlt_link_type { + NHLT_LINK_HDA = 0, + NHLT_LINK_DSP = 1, + NHLT_LINK_DMIC = 2, + NHLT_LINK_SSP = 3, + NHLT_LINK_INVALID +}; + +enum nhlt_device_type { + NHLT_DEVICE_BT = 0, + NHLT_DEVICE_DMIC = 1, + NHLT_DEVICE_I2S = 4, + NHLT_DEVICE_INVALID +}; + +struct nhlt_specific_cfg { + u32 size; + u8 caps[0]; +} __packed; + +struct nhlt_fmt_cfg { + struct wav_fmt_ext fmt_ext; + struct nhlt_specific_cfg config; +} __packed; + +struct nhlt_fmt { + u8 fmt_count; + struct nhlt_fmt_cfg fmt_config[0]; +} __packed; + +struct nhlt_endpoint { + u32 length; + u8 linktype; + u8 instance_id; + u16 vendor_id; + u16 device_id; + u16 revision_id; + u32 subsystem_id; + u8 device_type; + u8 direction; + u8 virtual_bus_id; + struct nhlt_specific_cfg config; +} __packed; + +struct nhlt_acpi_table { + struct acpi_table_header header; + u8 endpoint_count; + struct nhlt_endpoint desc[0]; +} __packed; + +struct nhlt_resource_desc { + u32 extra; + u16 flags; + u64 addr_spc_gra; + u64 min_addr; + u64 max_addr; + u64 addr_trans_offset; + u64 length; +} __packed; + +#endif diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c new file mode 100644 index 0000000..7d617bf --- /dev/null +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -0,0 +1,916 @@ +/* + * skl-pcm.c -ASoC HDA Platform driver file implementing PCM functionality + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "skl.h" + +#define HDA_MONO 1 +#define HDA_STEREO 2 + +static struct snd_pcm_hardware azx_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ + SNDRV_PCM_INFO_HAS_LINK_ATIME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = AZX_MAX_BUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = AZX_MAX_BUF_SIZE / 2, + .periods_min = 2, + .periods_max = AZX_MAX_FRAG, + .fifo_size = 0, +}; + +static inline +struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + +static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct hdac_stream *hstream = hdac_stream(stream); + struct hdac_bus *bus = hstream->bus; + + return hbus_to_ebus(bus); +} + +static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus, + struct snd_pcm_substream *substream, + size_t size) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + + hdac_stream(stream)->bufsize = 0; + hdac_stream(stream)->period_bytes = 0; + hdac_stream(stream)->format_val = 0; + + return snd_pcm_lib_malloc_pages(substream, size); +} + +static int skl_substream_free_pages(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, + struct snd_pcm_runtime *runtime) +{ + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + /* avoid wrap-around with wall-clock */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + 20, 178000000); +} + +static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus) +{ + if (ebus->ppcap) + return HDAC_EXT_STREAM_TYPE_HOST; + else + return HDAC_EXT_STREAM_TYPE_COUPLED; +} + +static int skl_pcm_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct skl_dma_params *dma_params; + int ret; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = pm_runtime_get_sync(dai->dev); + if (ret) + return ret; + + stream = snd_hdac_ext_stream_assign(ebus, substream, + skl_get_host_stream_type(ebus)); + if (stream == NULL) + return -EBUSY; + + skl_set_pcm_constrains(ebus, runtime); + + /* + * disable WALLCLOCK timestamps for capture streams + * until we figure out how to handle digital inputs + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; + } + + runtime->private_data = stream; + + dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL); + if (!dma_params) + return -ENOMEM; + + dma_params->stream_tag = hdac_stream(stream)->stream_tag; + snd_soc_dai_set_dma_data(dai, substream, dma_params); + + dev_dbg(dai->dev, "stream tag set in dma params=%d\n", + dma_params->stream_tag); + snd_pcm_set_sync(substream); + + return 0; +} + +static int skl_get_format(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct skl_dma_params *dma_params; + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + int format_val = 0; + + if (ebus->ppcap) { + struct snd_pcm_runtime *runtime = substream->runtime; + + format_val = snd_hdac_calc_stream_format(runtime->rate, + runtime->channels, + runtime->format, + 32, 0); + } else { + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); + if (dma_params) + format_val = dma_params->format; + } + + return format_val; +} + +static int skl_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + unsigned int format_val; + int err; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + if (hdac_stream(stream)->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + + format_val = skl_get_format(substream, dai); + dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", + hdac_stream(stream)->stream_tag, format_val); + snd_hdac_stream_reset(hdac_stream(stream)); + + err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); + if (err < 0) + return err; + + err = snd_hdac_stream_setup(hdac_stream(stream)); + if (err < 0) + return err; + + hdac_stream(stream)->prepared = 1; + + return err; +} + +static int skl_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret, dma_id; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = skl_substream_alloc_pages(ebus, substream, + params_buffer_bytes(params)); + if (ret < 0) + return ret; + + dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + + dma_id = hdac_stream(stream)->stream_tag - 1; + dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + + return 0; +} + +static void skl_pcm_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct skl_dma_params *dma_params = NULL; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(ebus)); + + dma_params = snd_soc_dai_get_dma_data(dai, substream); + /* + * now we should set this to NULL as we are freeing by the + * dma_params + */ + snd_soc_dai_set_dma_data(dai, substream, NULL); + + pm_runtime_mark_last_busy(dai->dev); + pm_runtime_put_autosuspend(dai->dev); + kfree(dma_params); +} + +static int skl_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + snd_hdac_stream_cleanup(hdac_stream(stream)); + hdac_stream(stream)->prepared = 0; + + return skl_substream_free_pages(ebus_to_hbus(ebus), substream); +} + +static int skl_link_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *link_dev; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct skl_dma_params *dma_params; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int dma_id; + + pr_debug("%s\n", __func__); + link_dev = snd_hdac_ext_stream_assign(ebus, substream, + HDAC_EXT_STREAM_TYPE_LINK); + if (!link_dev) + return -EBUSY; + + snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); + + /* set the stream tag in the codec dai dma params */ + dma_params = (struct skl_dma_params *) + snd_soc_dai_get_dma_data(codec_dai, substream); + if (dma_params) + dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; + snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); + dma_id = hdac_stream(link_dev)->stream_tag - 1; + + return 0; +} + +static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *link_dev = + snd_soc_dai_get_dma_data(dai, substream); + unsigned int format_val = 0; + struct skl_dma_params *dma_params; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_pcm_hw_params *params; + struct snd_interval *channels, *rate; + struct hdac_ext_link *link; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + if (link_dev->link_prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL); + if (params == NULL) + return -ENOMEM; + + channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = substream->runtime->channels; + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + rate->min = rate->max = substream->runtime->rate; + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + substream->runtime->format); + + + dma_params = (struct skl_dma_params *) + snd_soc_dai_get_dma_data(codec_dai, substream); + if (dma_params) + format_val = dma_params->format; + dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n", + hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name); + + snd_hdac_ext_link_stream_reset(link_dev); + + snd_hdac_ext_link_stream_setup(link_dev, format_val); + + link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); + if (!link) + return -EINVAL; + + snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag); + link_dev->link_prepared = 1; + + return 0; +} + +static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *link_dev = + snd_soc_dai_get_dma_data(dai, substream); + + dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + snd_hdac_ext_link_stream_start(link_dev); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + snd_hdac_ext_link_stream_clear(link_dev); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int skl_link_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_ext_stream *link_dev = + snd_soc_dai_get_dma_data(dai, substream); + struct hdac_ext_link *link; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + link_dev->link_prepared = 0; + + link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); + if (!link) + return -EINVAL; + + snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag); + snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); + return 0; +} + +static int skl_hda_be_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return pm_runtime_get_sync(dai->dev); +} + +static void skl_hda_be_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pm_runtime_mark_last_busy(dai->dev); + pm_runtime_put_autosuspend(dai->dev); +} + +static struct snd_soc_dai_ops skl_pcm_dai_ops = { + .startup = skl_pcm_open, + .shutdown = skl_pcm_close, + .prepare = skl_pcm_prepare, + .hw_params = skl_pcm_hw_params, + .hw_free = skl_pcm_hw_free, +}; + +static struct snd_soc_dai_ops skl_dmic_dai_ops = { + .startup = skl_hda_be_startup, + .shutdown = skl_hda_be_shutdown, +}; + +static struct snd_soc_dai_ops skl_link_dai_ops = { + .startup = skl_hda_be_startup, + .prepare = skl_link_pcm_prepare, + .hw_params = skl_link_hw_params, + .hw_free = skl_link_hw_free, + .trigger = skl_link_pcm_trigger, + .shutdown = skl_hda_be_shutdown, +}; + +static struct snd_soc_dai_driver skl_platform_dai[] = { +{ + .name = "System Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "System Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "System Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Reference Pin", + .ops = &skl_pcm_dai_ops, + .capture = { + .stream_name = "Reference Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Deepbuffer Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "Deepbuffer Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "LowLatency Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "Low Latency Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +/* BE CPU Dais */ +{ + .name = "iDisp Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "iDisp Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "DMIC01 Pin", + .ops = &skl_dmic_dai_ops, + .capture = { + .stream_name = "DMIC01 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "DMIC23 Pin", + .ops = &skl_dmic_dai_ops, + .capture = { + .stream_name = "DMIC23 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "HD-Codec Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "HD-Codec Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "HD-Codec Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "HD-Codec-SPK Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "HD-Codec-SPK Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "HD-Codec-AMIC Pin", + .ops = &skl_link_dai_ops, + .capture = { + .stream_name = "HD-Codec-AMIC Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +}; + +static int skl_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__, + dai_link->cpu_dai_name); + + runtime = substream->runtime; + snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw); + + return 0; +} + +static int skl_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *stream; + struct snd_pcm_substream *s; + bool start; + int sbits = 0; + unsigned long cookie; + struct hdac_stream *hstr; + + stream = get_hdac_ext_stream(substream); + hstr = hdac_stream(stream); + + dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = true; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = false; + break; + + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + stream = get_hdac_ext_stream(s); + sbits |= 1 << hdac_stream(stream)->index; + snd_pcm_trigger_done(s, substream); + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + /* first, set SYNC bits of corresponding streams */ + snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC); + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + stream = get_hdac_ext_stream(s); + if (start) + snd_hdac_stream_start(hdac_stream(stream), true); + else + snd_hdac_stream_stop(hdac_stream(stream)); + } + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + snd_hdac_stream_sync(hstr, start, sbits); + + spin_lock_irqsave(&bus->reg_lock, cookie); + + /* reset SYNC bits */ + snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC); + if (start) + snd_hdac_stream_timecounter_init(hstr, sbits); + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + return 0; +} + +static int skl_dsp_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct hdac_ext_stream *stream; + int start; + unsigned long cookie; + struct hdac_stream *hstr; + + dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name); + + stream = get_hdac_ext_stream(substream); + hstr = hdac_stream(stream); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = 1; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = 0; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + if (start) + snd_hdac_stream_start(hdac_stream(stream), true); + else + snd_hdac_stream_stop(hdac_stream(stream)); + + if (start) + snd_hdac_stream_timecounter_init(hstr, 0); + + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + return 0; +} +static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + + if (ebus->ppcap) + return skl_dsp_trigger(substream, cmd); + else + return skl_pcm_trigger(substream, cmd); +} + +/* calculate runtime delay from LPIB */ +static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *sstream, + unsigned int pos) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *hstream = hdac_stream(sstream); + struct snd_pcm_substream *substream = hstream->substream; + int stream = substream->stream; + unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream); + int delay; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = pos - lpib_pos; + else + delay = lpib_pos - pos; + + if (delay < 0) { + if (delay >= hstream->delay_negative_threshold) + delay = 0; + else + delay += hstream->bufsize; + } + + if (delay >= hstream->period_bytes) { + dev_info(bus->dev, + "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", + delay, hstream->period_bytes); + delay = 0; + } + + return bytes_to_frames(substream->runtime, delay); +} + +static unsigned int skl_get_position(struct hdac_ext_stream *hstream, + int codec_delay) +{ + struct hdac_stream *hstr = hdac_stream(hstream); + struct snd_pcm_substream *substream = hstr->substream; + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + unsigned int pos; + int delay; + + /* use the position buffer as default */ + pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); + + if (pos >= hdac_stream(hstream)->bufsize) + pos = 0; + + if (substream->runtime) { + delay = skl_get_delay_from_lpib(ebus, hstream, pos) + + codec_delay; + substream->runtime->delay += delay; + } + + return pos; +} + +static snd_pcm_uframes_t skl_platform_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); + + return bytes_to_frames(substream->runtime, + skl_get_position(hstream, 0)); +} + +static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, + u64 nsec) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + u64 codec_frames, codec_nsecs; + + if (!codec_dai->driver->ops->delay) + return nsec; + + codec_frames = codec_dai->driver->ops->delay(substream, codec_dai); + codec_nsecs = div_u64(codec_frames * 1000000000LL, + substream->runtime->rate); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return nsec + codec_nsecs; + + return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0; +} + +static int skl_get_time_info(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) +{ + struct hdac_ext_stream *sstream = get_hdac_ext_stream(substream); + struct hdac_stream *hstr = hdac_stream(sstream); + u64 nsec; + + if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && + (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { + + snd_pcm_gettime(substream->runtime, system_ts); + + nsec = timecounter_read(&hstr->tc); + nsec = div_u64(nsec, 3); /* can be optimized */ + if (audio_tstamp_config->report_delay) + nsec = skl_adjust_codec_delay(substream, nsec); + + *audio_ts = ns_to_timespec(nsec); + + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */ + audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */ + + } else { + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + } + + return 0; +} + +static struct snd_pcm_ops skl_platform_ops = { + .open = skl_platform_open, + .ioctl = snd_pcm_lib_ioctl, + .trigger = skl_platform_pcm_trigger, + .pointer = skl_platform_pcm_pointer, + .get_time_info = skl_get_time_info, + .mmap = snd_pcm_lib_default_mmap, + .page = snd_pcm_sgbuf_ops_page, +}; + +static void skl_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) + +static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->cpu_dai; + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct snd_pcm *pcm = rtd->pcm; + unsigned int size; + int retval = 0; + struct skl *skl = ebus_to_skl(ebus); + + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + /* buffer pre-allocation */ + size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; + if (size > MAX_PREALLOC_SIZE) + size = MAX_PREALLOC_SIZE; + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(skl->pci), + size, MAX_PREALLOC_SIZE); + if (retval) { + dev_err(dai->dev, "dma buffer allocationf fail\n"); + return retval; + } + } + + return retval; +} + +static struct snd_soc_platform_driver skl_platform_drv = { + .ops = &skl_platform_ops, + .pcm_new = skl_pcm_new, + .pcm_free = skl_pcm_free, +}; + +static const struct snd_soc_component_driver skl_component = { + .name = "pcm", +}; + +int skl_platform_register(struct device *dev) +{ + int ret; + + ret = snd_soc_register_platform(dev, &skl_platform_drv); + if (ret) { + dev_err(dev, "soc platform registration failed %d\n", ret); + return ret; + } + ret = snd_soc_register_component(dev, &skl_component, + skl_platform_dai, + ARRAY_SIZE(skl_platform_dai)); + if (ret) { + dev_err(dev, "soc component registration failed %d\n", ret); + snd_soc_unregister_platform(dev); + } + + return ret; + +} + +int skl_platform_unregister(struct device *dev) +{ + snd_soc_unregister_component(dev); + snd_soc_unregister_platform(dev); + return 0; +} diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c new file mode 100644 index 0000000..44748ba --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -0,0 +1,327 @@ +/* + * skl-sst-cldma.c - Code Loader DMA handler + * + * Copyright (C) 2015, Intel Corporation. + * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/mm.h> +#include <linux/kthread.h> +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" + +static void skl_cldma_int_enable(struct sst_dsp *ctx) +{ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC, + SKL_ADSPIC_CL_DMA, SKL_ADSPIC_CL_DMA); +} + +void skl_cldma_int_disable(struct sst_dsp *ctx) +{ + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_ADSPIC, SKL_ADSPIC_CL_DMA, 0); +} + +/* Code loader helper APIs */ +static void skl_cldma_setup_bdle(struct sst_dsp *ctx, + struct snd_dma_buffer *dmab_data, + u32 **bdlp, int size, int with_ioc) +{ + u32 *bdl = *bdlp; + + ctx->cl_dev.frags = 0; + while (size > 0) { + phys_addr_t addr = virt_to_phys(dmab_data->area + + (ctx->cl_dev.frags * ctx->cl_dev.bufsize)); + + bdl[0] = cpu_to_le32(lower_32_bits(addr)); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + + bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize); + + size -= ctx->cl_dev.bufsize; + bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + + bdl += 4; + ctx->cl_dev.frags++; + } +} + +/* + * Setup controller + * Configure the registers to update the dma buffer address and + * enable interrupts. + * Note: Using the channel 1 for transfer + */ +static void skl_cldma_setup_controller(struct sst_dsp *ctx, + struct snd_dma_buffer *dmab_bdl, unsigned int max_size, + u32 count) +{ + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, + CL_SD_BDLPLBA(dmab_bdl->addr)); + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, + CL_SD_BDLPUBA(dmab_bdl->addr)); + + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, max_size); + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, count - 1); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(1)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(1)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(1)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(FW_CL_STREAM_NUMBER)); +} + +static void skl_cldma_setup_spb(struct sst_dsp *ctx, + unsigned int size, bool enable) +{ + if (enable) + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL, + CL_SPBFIFO_SPBFCCTL_SPIBE_MASK, + CL_SPBFIFO_SPBFCCTL_SPIBE(1)); + + sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, size); +} + +static void skl_cldma_cleanup_spb(struct sst_dsp *ctx) +{ + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL, + CL_SPBFIFO_SPBFCCTL_SPIBE_MASK, + CL_SPBFIFO_SPBFCCTL_SPIBE(0)); + + sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, 0); +} + +static void skl_cldma_trigger(struct sst_dsp *ctx, bool enable) +{ + if (enable) + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(1)); + else + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(0)); +} + +static void skl_cldma_cleanup(struct sst_dsp *ctx) +{ + skl_cldma_cleanup_spb(ctx); + + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0)); + + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0)); + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0); + + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0); + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0); +} + +static int skl_cldma_wait_interruptible(struct sst_dsp *ctx) +{ + int ret = 0; + + if (!wait_event_timeout(ctx->cl_dev.wait_queue, + ctx->cl_dev.wait_condition, + msecs_to_jiffies(SKL_WAIT_TIMEOUT))) { + dev_err(ctx->dev, "%s: Wait timeout\n", __func__); + ret = -EIO; + goto cleanup; + } + + dev_dbg(ctx->dev, "%s: Event wake\n", __func__); + if (ctx->cl_dev.wake_status != SKL_CL_DMA_BUF_COMPLETE) { + dev_err(ctx->dev, "%s: DMA Error\n", __func__); + ret = -EIO; + } + +cleanup: + ctx->cl_dev.wake_status = SKL_CL_DMA_STATUS_NONE; + return ret; +} + +static void skl_cldma_stop(struct sst_dsp *ctx) +{ + ctx->cl_dev.ops.cl_trigger(ctx, false); +} + +static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size, + const void *curr_pos, bool intr_enable, bool trigger) +{ + dev_dbg(ctx->dev, "Size: %x, intr_enable: %d\n", size, intr_enable); + dev_dbg(ctx->dev, "buf_pos_index:%d, trigger:%d\n", + ctx->cl_dev.dma_buffer_offset, trigger); + dev_dbg(ctx->dev, "spib position: %d\n", ctx->cl_dev.curr_spib_pos); + + memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset, + curr_pos, size); + + if (ctx->cl_dev.curr_spib_pos == ctx->cl_dev.bufsize) + ctx->cl_dev.dma_buffer_offset = 0; + else + ctx->cl_dev.dma_buffer_offset = ctx->cl_dev.curr_spib_pos; + + ctx->cl_dev.wait_condition = false; + + if (intr_enable) + skl_cldma_int_enable(ctx); + + ctx->cl_dev.ops.cl_setup_spb(ctx, ctx->cl_dev.curr_spib_pos, trigger); + if (trigger) + ctx->cl_dev.ops.cl_trigger(ctx, true); +} + +/* + * The CL dma doesn't have any way to update the transfer status until a BDL + * buffer is fully transferred + * + * So Copying is divided in two parts. + * 1. Interrupt on buffer done where the size to be transferred is more than + * ring buffer size. + * 2. Polling on fw register to identify if data left to transferred doesn't + * fill the ring buffer. Caller takes care of polling the required status + * register to identify the transfer status. + */ +static int +skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size) +{ + int ret = 0; + bool start = true; + unsigned int excess_bytes; + u32 size; + unsigned int bytes_left = total_size; + const void *curr_pos = bin; + + if (total_size <= 0) + return -EINVAL; + + dev_dbg(ctx->dev, "%s: Total binary size: %u\n", __func__, bytes_left); + + while (bytes_left) { + if (bytes_left > ctx->cl_dev.bufsize) { + + /* + * dma transfers only till the write pointer as + * updated in spib + */ + if (ctx->cl_dev.curr_spib_pos == 0) + ctx->cl_dev.curr_spib_pos = ctx->cl_dev.bufsize; + + size = ctx->cl_dev.bufsize; + skl_cldma_fill_buffer(ctx, size, curr_pos, true, start); + + start = false; + ret = skl_cldma_wait_interruptible(ctx); + if (ret < 0) { + skl_cldma_stop(ctx); + return ret; + } + + } else { + skl_cldma_int_disable(ctx); + + if ((ctx->cl_dev.curr_spib_pos + bytes_left) + <= ctx->cl_dev.bufsize) { + ctx->cl_dev.curr_spib_pos += bytes_left; + } else { + excess_bytes = bytes_left - + (ctx->cl_dev.bufsize - + ctx->cl_dev.curr_spib_pos); + ctx->cl_dev.curr_spib_pos = excess_bytes; + } + + size = bytes_left; + skl_cldma_fill_buffer(ctx, size, + curr_pos, false, start); + } + bytes_left -= size; + curr_pos = curr_pos + size; + } + + return ret; +} + +void skl_cldma_process_intr(struct sst_dsp *ctx) +{ + u8 cl_dma_intr_status; + + cl_dma_intr_status = + sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_CL_SD_STS); + + if (!(cl_dma_intr_status & SKL_CL_DMA_SD_INT_COMPLETE)) + ctx->cl_dev.wake_status = SKL_CL_DMA_ERR; + else + ctx->cl_dev.wake_status = SKL_CL_DMA_BUF_COMPLETE; + + ctx->cl_dev.wait_condition = true; + wake_up(&ctx->cl_dev.wait_queue); +} + +int skl_cldma_prepare(struct sst_dsp *ctx) +{ + int ret; + u32 *bdl; + + ctx->cl_dev.bufsize = SKL_MAX_BUFFER_SIZE; + + /* Allocate cl ops */ + ctx->cl_dev.ops.cl_setup_bdle = skl_cldma_setup_bdle; + ctx->cl_dev.ops.cl_setup_controller = skl_cldma_setup_controller; + ctx->cl_dev.ops.cl_setup_spb = skl_cldma_setup_spb; + ctx->cl_dev.ops.cl_cleanup_spb = skl_cldma_cleanup_spb; + ctx->cl_dev.ops.cl_trigger = skl_cldma_trigger; + ctx->cl_dev.ops.cl_cleanup_controller = skl_cldma_cleanup; + ctx->cl_dev.ops.cl_copy_to_dmabuf = skl_cldma_copy_to_buf; + ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop; + + /* Allocate buffer*/ + ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, + &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); + if (ret < 0) { + dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret); + return ret; + } + /* Setup Code loader BDL */ + ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, + &ctx->cl_dev.dmab_bdl, PAGE_SIZE); + if (ret < 0) { + dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret); + ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); + return ret; + } + bdl = (u32 *)ctx->cl_dev.dmab_bdl.area; + + /* Allocate BDLs */ + ctx->cl_dev.ops.cl_setup_bdle(ctx, &ctx->cl_dev.dmab_data, + &bdl, ctx->cl_dev.bufsize, 1); + ctx->cl_dev.ops.cl_setup_controller(ctx, &ctx->cl_dev.dmab_bdl, + ctx->cl_dev.bufsize, ctx->cl_dev.frags); + + ctx->cl_dev.curr_spib_pos = 0; + ctx->cl_dev.dma_buffer_offset = 0; + init_waitqueue_head(&ctx->cl_dev.wait_queue); + + return ret; +} diff --git a/sound/soc/intel/skylake/skl-sst-cldma.h b/sound/soc/intel/skylake/skl-sst-cldma.h new file mode 100644 index 0000000..99e4c86 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-cldma.h @@ -0,0 +1,251 @@ +/* + * Intel Code Loader DMA support + * + * Copyright (C) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef SKL_SST_CLDMA_H_ +#define SKL_SST_CLDMA_H_ + +#define FW_CL_STREAM_NUMBER 0x1 + +#define DMA_ADDRESS_128_BITS_ALIGNMENT 7 +#define BDL_ALIGN(x) (x >> DMA_ADDRESS_128_BITS_ALIGNMENT) + +#define SKL_ADSPIC_CL_DMA 0x2 +#define SKL_ADSPIS_CL_DMA 0x2 +#define SKL_CL_DMA_SD_INT_DESC_ERR 0x10 /* Descriptor error interrupt */ +#define SKL_CL_DMA_SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define SKL_CL_DMA_SD_INT_COMPLETE 0x04 /* Buffer completion interrupt */ + +/* Intel HD Audio Code Loader DMA Registers */ + +#define HDA_ADSP_LOADER_BASE 0x80 + +/* Stream Registers */ +#define SKL_ADSP_REG_CL_SD_CTL (HDA_ADSP_LOADER_BASE + 0x00) +#define SKL_ADSP_REG_CL_SD_STS (HDA_ADSP_LOADER_BASE + 0x03) +#define SKL_ADSP_REG_CL_SD_LPIB (HDA_ADSP_LOADER_BASE + 0x04) +#define SKL_ADSP_REG_CL_SD_CBL (HDA_ADSP_LOADER_BASE + 0x08) +#define SKL_ADSP_REG_CL_SD_LVI (HDA_ADSP_LOADER_BASE + 0x0c) +#define SKL_ADSP_REG_CL_SD_FIFOW (HDA_ADSP_LOADER_BASE + 0x0e) +#define SKL_ADSP_REG_CL_SD_FIFOSIZE (HDA_ADSP_LOADER_BASE + 0x10) +#define SKL_ADSP_REG_CL_SD_FORMAT (HDA_ADSP_LOADER_BASE + 0x12) +#define SKL_ADSP_REG_CL_SD_FIFOL (HDA_ADSP_LOADER_BASE + 0x14) +#define SKL_ADSP_REG_CL_SD_BDLPL (HDA_ADSP_LOADER_BASE + 0x18) +#define SKL_ADSP_REG_CL_SD_BDLPU (HDA_ADSP_LOADER_BASE + 0x1c) + +/* CL: Software Position Based FIFO Capability Registers */ +#define SKL_ADSP_REG_CL_SPBFIFO (HDA_ADSP_LOADER_BASE + 0x20) +#define SKL_ADSP_REG_CL_SPBFIFO_SPBFCH (SKL_ADSP_REG_CL_SPBFIFO + 0x0) +#define SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL (SKL_ADSP_REG_CL_SPBFIFO + 0x4) +#define SKL_ADSP_REG_CL_SPBFIFO_SPIB (SKL_ADSP_REG_CL_SPBFIFO + 0x8) +#define SKL_ADSP_REG_CL_SPBFIFO_MAXFIFOS (SKL_ADSP_REG_CL_SPBFIFO + 0xc) + +/* CL: Stream Descriptor x Control */ + +/* Stream Reset */ +#define CL_SD_CTL_SRST_SHIFT 0 +#define CL_SD_CTL_SRST_MASK (1 << CL_SD_CTL_SRST_SHIFT) +#define CL_SD_CTL_SRST(x) \ + ((x << CL_SD_CTL_SRST_SHIFT) & CL_SD_CTL_SRST_MASK) + +/* Stream Run */ +#define CL_SD_CTL_RUN_SHIFT 1 +#define CL_SD_CTL_RUN_MASK (1 << CL_SD_CTL_RUN_SHIFT) +#define CL_SD_CTL_RUN(x) \ + ((x << CL_SD_CTL_RUN_SHIFT) & CL_SD_CTL_RUN_MASK) + +/* Interrupt On Completion Enable */ +#define CL_SD_CTL_IOCE_SHIFT 2 +#define CL_SD_CTL_IOCE_MASK (1 << CL_SD_CTL_IOCE_SHIFT) +#define CL_SD_CTL_IOCE(x) \ + ((x << CL_SD_CTL_IOCE_SHIFT) & CL_SD_CTL_IOCE_MASK) + +/* FIFO Error Interrupt Enable */ +#define CL_SD_CTL_FEIE_SHIFT 3 +#define CL_SD_CTL_FEIE_MASK (1 << CL_SD_CTL_FEIE_SHIFT) +#define CL_SD_CTL_FEIE(x) \ + ((x << CL_SD_CTL_FEIE_SHIFT) & CL_SD_CTL_FEIE_MASK) + +/* Descriptor Error Interrupt Enable */ +#define CL_SD_CTL_DEIE_SHIFT 4 +#define CL_SD_CTL_DEIE_MASK (1 << CL_SD_CTL_DEIE_SHIFT) +#define CL_SD_CTL_DEIE(x) \ + ((x << CL_SD_CTL_DEIE_SHIFT) & CL_SD_CTL_DEIE_MASK) + +/* FIFO Limit Change */ +#define CL_SD_CTL_FIFOLC_SHIFT 5 +#define CL_SD_CTL_FIFOLC_MASK (1 << CL_SD_CTL_FIFOLC_SHIFT) +#define CL_SD_CTL_FIFOLC(x) \ + ((x << CL_SD_CTL_FIFOLC_SHIFT) & CL_SD_CTL_FIFOLC_MASK) + +/* Stripe Control */ +#define CL_SD_CTL_STRIPE_SHIFT 16 +#define CL_SD_CTL_STRIPE_MASK (0x3 << CL_SD_CTL_STRIPE_SHIFT) +#define CL_SD_CTL_STRIPE(x) \ + ((x << CL_SD_CTL_STRIPE_SHIFT) & CL_SD_CTL_STRIPE_MASK) + +/* Traffic Priority */ +#define CL_SD_CTL_TP_SHIFT 18 +#define CL_SD_CTL_TP_MASK (1 << CL_SD_CTL_TP_SHIFT) +#define CL_SD_CTL_TP(x) \ + ((x << CL_SD_CTL_TP_SHIFT) & CL_SD_CTL_TP_MASK) + +/* Bidirectional Direction Control */ +#define CL_SD_CTL_DIR_SHIFT 19 +#define CL_SD_CTL_DIR_MASK (1 << CL_SD_CTL_DIR_SHIFT) +#define CL_SD_CTL_DIR(x) \ + ((x << CL_SD_CTL_DIR_SHIFT) & CL_SD_CTL_DIR_MASK) + +/* Stream Number */ +#define CL_SD_CTL_STRM_SHIFT 20 +#define CL_SD_CTL_STRM_MASK (0xf << CL_SD_CTL_STRM_SHIFT) +#define CL_SD_CTL_STRM(x) \ + ((x << CL_SD_CTL_STRM_SHIFT) & CL_SD_CTL_STRM_MASK) + +/* CL: Stream Descriptor x Status */ + +/* Buffer Completion Interrupt Status */ +#define CL_SD_STS_BCIS(x) CL_SD_CTL_IOCE(x) + +/* FIFO Error */ +#define CL_SD_STS_FIFOE(x) CL_SD_CTL_FEIE(x) + +/* Descriptor Error */ +#define CL_SD_STS_DESE(x) CL_SD_CTL_DEIE(x) + +/* FIFO Ready */ +#define CL_SD_STS_FIFORDY(x) CL_SD_CTL_FIFOLC(x) + + +/* CL: Stream Descriptor x Last Valid Index */ +#define CL_SD_LVI_SHIFT 0 +#define CL_SD_LVI_MASK (0xff << CL_SD_LVI_SHIFT) +#define CL_SD_LVI(x) ((x << CL_SD_LVI_SHIFT) & CL_SD_LVI_MASK) + +/* CL: Stream Descriptor x FIFO Eviction Watermark */ +#define CL_SD_FIFOW_SHIFT 0 +#define CL_SD_FIFOW_MASK (0x7 << CL_SD_FIFOW_SHIFT) +#define CL_SD_FIFOW(x) \ + ((x << CL_SD_FIFOW_SHIFT) & CL_SD_FIFOW_MASK) + +/* CL: Stream Descriptor x Buffer Descriptor List Pointer Lower Base Address */ + +/* Protect Bits */ +#define CL_SD_BDLPLBA_PROT_SHIFT 0 +#define CL_SD_BDLPLBA_PROT_MASK (1 << CL_SD_BDLPLBA_PROT_SHIFT) +#define CL_SD_BDLPLBA_PROT(x) \ + ((x << CL_SD_BDLPLBA_PROT_SHIFT) & CL_SD_BDLPLBA_PROT_MASK) + +/* Buffer Descriptor List Lower Base Address */ +#define CL_SD_BDLPLBA_SHIFT 7 +#define CL_SD_BDLPLBA_MASK (0x1ffffff << CL_SD_BDLPLBA_SHIFT) +#define CL_SD_BDLPLBA(x) \ + ((BDL_ALIGN(lower_32_bits(x)) << CL_SD_BDLPLBA_SHIFT) & CL_SD_BDLPLBA_MASK) + +/* Buffer Descriptor List Upper Base Address */ +#define CL_SD_BDLPUBA_SHIFT 0 +#define CL_SD_BDLPUBA_MASK (0xffffffff << CL_SD_BDLPUBA_SHIFT) +#define CL_SD_BDLPUBA(x) \ + ((upper_32_bits(x) << CL_SD_BDLPUBA_SHIFT) & CL_SD_BDLPUBA_MASK) + +/* + * Code Loader - Software Position Based FIFO + * Capability Registers x Software Position Based FIFO Header + */ + +/* Next Capability Pointer */ +#define CL_SPBFIFO_SPBFCH_PTR_SHIFT 0 +#define CL_SPBFIFO_SPBFCH_PTR_MASK (0xff << CL_SPBFIFO_SPBFCH_PTR_SHIFT) +#define CL_SPBFIFO_SPBFCH_PTR(x) \ + ((x << CL_SPBFIFO_SPBFCH_PTR_SHIFT) & CL_SPBFIFO_SPBFCH_PTR_MASK) + +/* Capability Identifier */ +#define CL_SPBFIFO_SPBFCH_ID_SHIFT 16 +#define CL_SPBFIFO_SPBFCH_ID_MASK (0xfff << CL_SPBFIFO_SPBFCH_ID_SHIFT) +#define CL_SPBFIFO_SPBFCH_ID(x) \ + ((x << CL_SPBFIFO_SPBFCH_ID_SHIFT) & CL_SPBFIFO_SPBFCH_ID_MASK) + +/* Capability Version */ +#define CL_SPBFIFO_SPBFCH_VER_SHIFT 28 +#define CL_SPBFIFO_SPBFCH_VER_MASK (0xf << CL_SPBFIFO_SPBFCH_VER_SHIFT) +#define CL_SPBFIFO_SPBFCH_VER(x) \ + ((x << CL_SPBFIFO_SPBFCH_VER_SHIFT) & CL_SPBFIFO_SPBFCH_VER_MASK) + +/* Software Position in Buffer Enable */ +#define CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0 +#define CL_SPBFIFO_SPBFCCTL_SPIBE_MASK (1 << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) +#define CL_SPBFIFO_SPBFCCTL_SPIBE(x) \ + ((x << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & CL_SPBFIFO_SPBFCCTL_SPIBE_MASK) + +/* SST IPC SKL defines */ +#define SKL_WAIT_TIMEOUT 500 /* 500 msec */ +#define SKL_MAX_BUFFER_SIZE (32 * PAGE_SIZE) + +enum skl_cl_dma_wake_states { + SKL_CL_DMA_STATUS_NONE = 0, + SKL_CL_DMA_BUF_COMPLETE, + SKL_CL_DMA_ERR, /* TODO: Expand the error states */ +}; + +struct sst_dsp; + +struct skl_cl_dev_ops { + void (*cl_setup_bdle)(struct sst_dsp *ctx, + struct snd_dma_buffer *dmab_data, + u32 **bdlp, int size, int with_ioc); + void (*cl_setup_controller)(struct sst_dsp *ctx, + struct snd_dma_buffer *dmab_bdl, + unsigned int max_size, u32 page_count); + void (*cl_setup_spb)(struct sst_dsp *ctx, + unsigned int size, bool enable); + void (*cl_cleanup_spb)(struct sst_dsp *ctx); + void (*cl_trigger)(struct sst_dsp *ctx, bool enable); + void (*cl_cleanup_controller)(struct sst_dsp *ctx); + int (*cl_copy_to_dmabuf)(struct sst_dsp *ctx, + const void *bin, u32 size); + void (*cl_stop_dma)(struct sst_dsp *ctx); +}; + +/** + * skl_cl_dev - holds information for code loader dma transfer + * + * @dmab_data: buffer pointer + * @dmab_bdl: buffer descriptor list + * @bufsize: ring buffer size + * @frags: Last valid buffer descriptor index in the BDL + * @curr_spib_pos: Current position in ring buffer + * @dma_buffer_offset: dma buffer offset + * @ops: operations supported on CL dma + * @wait_queue: wait queue to wake for wake event + * @wake_status: DMA wake status + * @wait_condition: condition to wait on wait queue + * @cl_dma_lock: for synchronized access to cldma + */ +struct skl_cl_dev { + struct snd_dma_buffer dmab_data; + struct snd_dma_buffer dmab_bdl; + + unsigned int bufsize; + unsigned int frags; + + unsigned int curr_spib_pos; + unsigned int dma_buffer_offset; + struct skl_cl_dev_ops ops; + + wait_queue_head_t wait_queue; + int wake_status; + bool wait_condition; +}; + +#endif /* SKL_SST_CLDMA_H_ */ diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c new file mode 100644 index 0000000..94875b0 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -0,0 +1,342 @@ +/* + * skl-sst-dsp.c - SKL SST library generic function + * + * Copyright (C) 2014-15, Intel Corporation. + * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> + * Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <sound/pcm.h> + +#include "../common/sst-dsp.h" +#include "../common/sst-ipc.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" + +/* various timeout values */ +#define SKL_DSP_PU_TO 50 +#define SKL_DSP_PD_TO 50 +#define SKL_DSP_RESET_TO 50 + +void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) +{ + mutex_lock(&ctx->mutex); + ctx->sst_state = state; + mutex_unlock(&ctx->mutex); +} + +static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) +{ + int ret; + + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK, + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)); + + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CRST_MASK, + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK), + SKL_DSP_RESET_TO, + "Set reset"); + if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) { + dev_err(ctx->dev, "Set reset state failed\n"); + ret = -EIO; + } + + return ret; +} + +static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) +{ + int ret; + + dev_dbg(ctx->dev, "In %s\n", __func__); + + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CRST_MASK, 0); + + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CRST_MASK, + 0, + SKL_DSP_RESET_TO, + "Unset reset"); + + if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) { + dev_err(ctx->dev, "Unset reset state failed\n"); + ret = -EIO; + } + + return ret; +} + +static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) +{ + int val; + bool is_enable; + + val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); + + is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) && + (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) && + !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) && + !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK))); + + dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable); + return is_enable; +} + +static int skl_dsp_reset_core(struct sst_dsp *ctx) +{ + /* stall core */ + sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); + + /* set reset state */ + return skl_dsp_core_set_reset_state(ctx); +} + +static int skl_dsp_start_core(struct sst_dsp *ctx) +{ + int ret; + + /* unset reset state */ + ret = skl_dsp_core_unset_reset_state(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp unset reset fails\n"); + return ret; + } + + /* run core */ + dev_dbg(ctx->dev, "run core...\n"); + sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); + + if (!is_skl_dsp_core_enable(ctx)) { + skl_dsp_reset_core(ctx); + dev_err(ctx->dev, "DSP core enable failed\n"); + ret = -EIO; + } + + return ret; +} + +static int skl_dsp_core_power_up(struct sst_dsp *ctx) +{ + int ret; + + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)); + + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CPA_MASK, + SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK), + SKL_DSP_PU_TO, + "Power up"); + + if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & + SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) != + SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) { + dev_err(ctx->dev, "DSP core power up failed\n"); + ret = -EIO; + } + + return ret; +} + +static int skl_dsp_core_power_down(struct sst_dsp *ctx) +{ + /* update bits */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_SPA_MASK, 0); + + /* poll with timeout to check if operation successful */ + return sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_SPA_MASK, + 0, + SKL_DSP_PD_TO, + "Power down"); +} + +static int skl_dsp_enable_core(struct sst_dsp *ctx) +{ + int ret; + + /* power up */ + ret = skl_dsp_core_power_up(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "dsp core power up failed\n"); + return ret; + } + + return skl_dsp_start_core(ctx); +} + +int skl_dsp_disable_core(struct sst_dsp *ctx) +{ + int ret; + + ret = skl_dsp_reset_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp core reset failed\n"); + return ret; + } + + /* power down core*/ + ret = skl_dsp_core_power_down(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp core power down failed\n"); + return ret; + } + + if (is_skl_dsp_core_enable(ctx)) { + dev_err(ctx->dev, "DSP core disable failed\n"); + ret = -EIO; + } + + return ret; +} + +int skl_dsp_boot(struct sst_dsp *ctx) +{ + int ret; + + if (is_skl_dsp_core_enable(ctx)) { + dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n"); + ret = skl_dsp_reset_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp reset failed\n"); + return ret; + } + + ret = skl_dsp_start_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "dsp start failed\n"); + return ret; + } + } else { + dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n"); + ret = skl_dsp_disable_core(ctx); + + if (ret < 0) { + dev_err(ctx->dev, "dsp disable core failes\n"); + return ret; + } + ret = skl_dsp_enable_core(ctx); + } + + return ret; +} + +irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) +{ + struct sst_dsp *ctx = dev_id; + u32 val; + irqreturn_t result = IRQ_NONE; + + spin_lock(&ctx->spinlock); + + val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); + ctx->intr_status = val; + + if (val & SKL_ADSPIS_IPC) { + skl_ipc_int_disable(ctx); + result = IRQ_WAKE_THREAD; + } + + if (val & SKL_ADSPIS_CL_DMA) { + skl_cldma_int_disable(ctx); + result = IRQ_WAKE_THREAD; + } + + spin_unlock(&ctx->spinlock); + + return result; +} + +int skl_dsp_wake(struct sst_dsp *ctx) +{ + return ctx->fw_ops.set_state_D0(ctx); +} +EXPORT_SYMBOL_GPL(skl_dsp_wake); + +int skl_dsp_sleep(struct sst_dsp *ctx) +{ + return ctx->fw_ops.set_state_D3(ctx); +} +EXPORT_SYMBOL_GPL(skl_dsp_sleep); + +struct sst_dsp *skl_dsp_ctx_init(struct device *dev, + struct sst_dsp_device *sst_dev, int irq) +{ + int ret; + struct sst_dsp *sst; + + sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); + if (sst == NULL) + return NULL; + + spin_lock_init(&sst->spinlock); + mutex_init(&sst->mutex); + sst->dev = dev; + sst->sst_dev = sst_dev; + sst->irq = irq; + sst->ops = sst_dev->ops; + sst->thread_context = sst_dev->thread_context; + + /* Initialise SST Audio DSP */ + if (sst->ops->init) { + ret = sst->ops->init(sst, NULL); + if (ret < 0) + return NULL; + } + + /* Register the ISR */ + ret = request_threaded_irq(sst->irq, sst->ops->irq_handler, + sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); + if (ret) { + dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n", + sst->irq); + return NULL; + } + + return sst; +} + +void skl_dsp_free(struct sst_dsp *dsp) +{ + skl_ipc_int_disable(dsp); + + free_irq(dsp->irq, dsp); + skl_dsp_disable_core(dsp); +} +EXPORT_SYMBOL_GPL(skl_dsp_free); + +bool is_skl_dsp_running(struct sst_dsp *ctx) +{ + return (ctx->sst_state == SKL_DSP_RUNNING); +} +EXPORT_SYMBOL_GPL(is_skl_dsp_running); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h new file mode 100644 index 0000000..6bfcef4 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -0,0 +1,145 @@ +/* + * Skylake SST DSP Support + * + * Copyright (C) 2014-15, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __SKL_SST_DSP_H__ +#define __SKL_SST_DSP_H__ + +#include <linux/interrupt.h> +#include <sound/memalloc.h> +#include "skl-sst-cldma.h" + +struct sst_dsp; +struct skl_sst; +struct sst_dsp_device; + +/* Intel HD Audio General DSP Registers */ +#define SKL_ADSP_GEN_BASE 0x0 +#define SKL_ADSP_REG_ADSPCS (SKL_ADSP_GEN_BASE + 0x04) +#define SKL_ADSP_REG_ADSPIC (SKL_ADSP_GEN_BASE + 0x08) +#define SKL_ADSP_REG_ADSPIS (SKL_ADSP_GEN_BASE + 0x0C) +#define SKL_ADSP_REG_ADSPIC2 (SKL_ADSP_GEN_BASE + 0x10) +#define SKL_ADSP_REG_ADSPIS2 (SKL_ADSP_GEN_BASE + 0x14) + +/* Intel HD Audio Inter-Processor Communication Registers */ +#define SKL_ADSP_IPC_BASE 0x40 +#define SKL_ADSP_REG_HIPCT (SKL_ADSP_IPC_BASE + 0x00) +#define SKL_ADSP_REG_HIPCTE (SKL_ADSP_IPC_BASE + 0x04) +#define SKL_ADSP_REG_HIPCI (SKL_ADSP_IPC_BASE + 0x08) +#define SKL_ADSP_REG_HIPCIE (SKL_ADSP_IPC_BASE + 0x0C) +#define SKL_ADSP_REG_HIPCCTL (SKL_ADSP_IPC_BASE + 0x10) + +/* HIPCI */ +#define SKL_ADSP_REG_HIPCI_BUSY BIT(31) + +/* HIPCIE */ +#define SKL_ADSP_REG_HIPCIE_DONE BIT(30) + +/* HIPCCTL */ +#define SKL_ADSP_REG_HIPCCTL_DONE BIT(1) +#define SKL_ADSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define SKL_ADSP_REG_HIPCT_BUSY BIT(31) + +/* Intel HD Audio SRAM Window 1 */ +#define SKL_ADSP_SRAM1_BASE 0xA000 + +#define SKL_ADSP_MMIO_LEN 0x10000 + +#define SKL_ADSP_W0_STAT_SZ 0x800 + +#define SKL_ADSP_W0_UP_SZ 0x800 + +#define SKL_ADSP_W1_SZ 0x1000 + +#define SKL_FW_STS_MASK 0xf + +#define SKL_FW_INIT 0x1 +#define SKL_FW_RFW_START 0xf + +#define SKL_ADSPIC_IPC 1 +#define SKL_ADSPIS_IPC 1 + +/* ADSPCS - Audio DSP Control & Status */ +#define SKL_DSP_CORES 1 +#define SKL_DSP_CORE0_MASK 1 +#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1) + +/* Core Reset - asserted high */ +#define SKL_ADSPCS_CRST_SHIFT 0 +#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT) +#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK) + +/* Core run/stall - when set to '1' core is stalled */ +#define SKL_ADSPCS_CSTALL_SHIFT 8 +#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \ + SKL_ADSPCS_CSTALL_SHIFT) +#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \ + SKL_ADSPCS_CSTALL_MASK) + +/* Set Power Active - when set to '1' turn cores on */ +#define SKL_ADSPCS_SPA_SHIFT 16 +#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT) +#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK) + +/* Current Power Active - power status of cores, set by hardware */ +#define SKL_ADSPCS_CPA_SHIFT 24 +#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT) +#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK) + +#define SST_DSP_POWER_D0 0x0 /* full On */ +#define SST_DSP_POWER_D3 0x3 /* Off */ + +enum skl_dsp_states { + SKL_DSP_RUNNING = 1, + SKL_DSP_RESET, +}; + +struct skl_dsp_fw_ops { + int (*load_fw)(struct sst_dsp *ctx); + /* FW module parser/loader */ + int (*parse_fw)(struct sst_dsp *ctx); + int (*set_state_D0)(struct sst_dsp *ctx); + int (*set_state_D3)(struct sst_dsp *ctx); + unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); +}; + +struct skl_dsp_loader_ops { + int (*alloc_dma_buf)(struct device *dev, + struct snd_dma_buffer *dmab, size_t size); + int (*free_dma_buf)(struct device *dev, + struct snd_dma_buffer *dmab); +}; + +void skl_cldma_process_intr(struct sst_dsp *ctx); +void skl_cldma_int_disable(struct sst_dsp *ctx); +int skl_cldma_prepare(struct sst_dsp *ctx); + +void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); +struct sst_dsp *skl_dsp_ctx_init(struct device *dev, + struct sst_dsp_device *sst_dev, int irq); +int skl_dsp_disable_core(struct sst_dsp *ctx); +bool is_skl_dsp_running(struct sst_dsp *ctx); +irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id); +int skl_dsp_wake(struct sst_dsp *ctx); +int skl_dsp_sleep(struct sst_dsp *ctx); +void skl_dsp_free(struct sst_dsp *dsp); + +int skl_dsp_boot(struct sst_dsp *ctx); +int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, + struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); +void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); + +#endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c new file mode 100644 index 0000000..937a0a3 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -0,0 +1,771 @@ +/* + * skl-sst-ipc.c - Intel skl IPC Support + * + * Copyright (C) 2014-15, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/device.h> + +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" + + +#define IPC_IXC_STATUS_BITS 24 + +/* Global Message - Generic */ +#define IPC_GLB_TYPE_SHIFT 24 +#define IPC_GLB_TYPE_MASK (0xf << IPC_GLB_TYPE_SHIFT) +#define IPC_GLB_TYPE(x) ((x) << IPC_GLB_TYPE_SHIFT) + +/* Global Message - Reply */ +#define IPC_GLB_REPLY_STATUS_SHIFT 24 +#define IPC_GLB_REPLY_STATUS_MASK ((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1) +#define IPC_GLB_REPLY_STATUS(x) ((x) << IPC_GLB_REPLY_STATUS_SHIFT) + +#define IPC_TIMEOUT_MSECS 3000 + +#define IPC_EMPTY_LIST_SIZE 8 + +#define IPC_MSG_TARGET_SHIFT 30 +#define IPC_MSG_TARGET_MASK 0x1 +#define IPC_MSG_TARGET(x) (((x) & IPC_MSG_TARGET_MASK) \ + << IPC_MSG_TARGET_SHIFT) + +#define IPC_MSG_DIR_SHIFT 29 +#define IPC_MSG_DIR_MASK 0x1 +#define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ + << IPC_MSG_DIR_SHIFT) +/* Global Notification Message */ +#define IPC_GLB_NOTIFY_TYPE_SHIFT 16 +#define IPC_GLB_NOTIFY_TYPE_MASK 0xFF +#define IPC_GLB_NOTIFY_TYPE(x) (((x) >> IPC_GLB_NOTIFY_TYPE_SHIFT) \ + & IPC_GLB_NOTIFY_TYPE_MASK) + +#define IPC_GLB_NOTIFY_MSG_TYPE_SHIFT 24 +#define IPC_GLB_NOTIFY_MSG_TYPE_MASK 0x1F +#define IPC_GLB_NOTIFY_MSG_TYPE(x) (((x) >> IPC_GLB_NOTIFY_MSG_TYPE_SHIFT) \ + & IPC_GLB_NOTIFY_MSG_TYPE_MASK) + +#define IPC_GLB_NOTIFY_RSP_SHIFT 29 +#define IPC_GLB_NOTIFY_RSP_MASK 0x1 +#define IPC_GLB_NOTIFY_RSP_TYPE(x) (((x) >> IPC_GLB_NOTIFY_RSP_SHIFT) \ + & IPC_GLB_NOTIFY_RSP_MASK) + +/* Pipeline operations */ + +/* Create pipeline message */ +#define IPC_PPL_MEM_SIZE_SHIFT 0 +#define IPC_PPL_MEM_SIZE_MASK 0x7FF +#define IPC_PPL_MEM_SIZE(x) (((x) & IPC_PPL_MEM_SIZE_MASK) \ + << IPC_PPL_MEM_SIZE_SHIFT) + +#define IPC_PPL_TYPE_SHIFT 11 +#define IPC_PPL_TYPE_MASK 0x1F +#define IPC_PPL_TYPE(x) (((x) & IPC_PPL_TYPE_MASK) \ + << IPC_PPL_TYPE_SHIFT) + +#define IPC_INSTANCE_ID_SHIFT 16 +#define IPC_INSTANCE_ID_MASK 0xFF +#define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \ + << IPC_INSTANCE_ID_SHIFT) + +/* Set pipeline state message */ +#define IPC_PPL_STATE_SHIFT 0 +#define IPC_PPL_STATE_MASK 0x1F +#define IPC_PPL_STATE(x) (((x) & IPC_PPL_STATE_MASK) \ + << IPC_PPL_STATE_SHIFT) + +/* Module operations primary register */ +#define IPC_MOD_ID_SHIFT 0 +#define IPC_MOD_ID_MASK 0xFFFF +#define IPC_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \ + << IPC_MOD_ID_SHIFT) + +#define IPC_MOD_INSTANCE_ID_SHIFT 16 +#define IPC_MOD_INSTANCE_ID_MASK 0xFF +#define IPC_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \ + << IPC_MOD_INSTANCE_ID_SHIFT) + +/* Init instance message extension register */ +#define IPC_PARAM_BLOCK_SIZE_SHIFT 0 +#define IPC_PARAM_BLOCK_SIZE_MASK 0xFFFF +#define IPC_PARAM_BLOCK_SIZE(x) (((x) & IPC_PARAM_BLOCK_SIZE_MASK) \ + << IPC_PARAM_BLOCK_SIZE_SHIFT) + +#define IPC_PPL_INSTANCE_ID_SHIFT 16 +#define IPC_PPL_INSTANCE_ID_MASK 0xFF +#define IPC_PPL_INSTANCE_ID(x) (((x) & IPC_PPL_INSTANCE_ID_MASK) \ + << IPC_PPL_INSTANCE_ID_SHIFT) + +#define IPC_CORE_ID_SHIFT 24 +#define IPC_CORE_ID_MASK 0x1F +#define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \ + << IPC_CORE_ID_SHIFT) + +/* Bind/Unbind message extension register */ +#define IPC_DST_MOD_ID_SHIFT 0 +#define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \ + << IPC_DST_MOD_ID_SHIFT) + +#define IPC_DST_MOD_INSTANCE_ID_SHIFT 16 +#define IPC_DST_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \ + << IPC_DST_MOD_INSTANCE_ID_SHIFT) + +#define IPC_DST_QUEUE_SHIFT 24 +#define IPC_DST_QUEUE_MASK 0x7 +#define IPC_DST_QUEUE(x) (((x) & IPC_DST_QUEUE_MASK) \ + << IPC_DST_QUEUE_SHIFT) + +#define IPC_SRC_QUEUE_SHIFT 27 +#define IPC_SRC_QUEUE_MASK 0x7 +#define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \ + << IPC_SRC_QUEUE_SHIFT) + +/* Save pipeline messgae extension register */ +#define IPC_DMA_ID_SHIFT 0 +#define IPC_DMA_ID_MASK 0x1F +#define IPC_DMA_ID(x) (((x) & IPC_DMA_ID_MASK) \ + << IPC_DMA_ID_SHIFT) +/* Large Config message extension register */ +#define IPC_DATA_OFFSET_SZ_SHIFT 0 +#define IPC_DATA_OFFSET_SZ_MASK 0xFFFFF +#define IPC_DATA_OFFSET_SZ(x) (((x) & IPC_DATA_OFFSET_SZ_MASK) \ + << IPC_DATA_OFFSET_SZ_SHIFT) +#define IPC_DATA_OFFSET_SZ_CLEAR ~(IPC_DATA_OFFSET_SZ_MASK \ + << IPC_DATA_OFFSET_SZ_SHIFT) + +#define IPC_LARGE_PARAM_ID_SHIFT 20 +#define IPC_LARGE_PARAM_ID_MASK 0xFF +#define IPC_LARGE_PARAM_ID(x) (((x) & IPC_LARGE_PARAM_ID_MASK) \ + << IPC_LARGE_PARAM_ID_SHIFT) + +#define IPC_FINAL_BLOCK_SHIFT 28 +#define IPC_FINAL_BLOCK_MASK 0x1 +#define IPC_FINAL_BLOCK(x) (((x) & IPC_FINAL_BLOCK_MASK) \ + << IPC_FINAL_BLOCK_SHIFT) + +#define IPC_INITIAL_BLOCK_SHIFT 29 +#define IPC_INITIAL_BLOCK_MASK 0x1 +#define IPC_INITIAL_BLOCK(x) (((x) & IPC_INITIAL_BLOCK_MASK) \ + << IPC_INITIAL_BLOCK_SHIFT) +#define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \ + << IPC_INITIAL_BLOCK_SHIFT) + +enum skl_ipc_msg_target { + IPC_FW_GEN_MSG = 0, + IPC_MOD_MSG = 1 +}; + +enum skl_ipc_msg_direction { + IPC_MSG_REQUEST = 0, + IPC_MSG_REPLY = 1 +}; + +/* Global Message Types */ +enum skl_ipc_glb_type { + IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */ + IPC_GLB_LOAD_MULTIPLE_MODS = 15, + IPC_GLB_UNLOAD_MULTIPLE_MODS = 16, + IPC_GLB_CREATE_PPL = 17, + IPC_GLB_DELETE_PPL = 18, + IPC_GLB_SET_PPL_STATE = 19, + IPC_GLB_GET_PPL_STATE = 20, + IPC_GLB_GET_PPL_CONTEXT_SIZE = 21, + IPC_GLB_SAVE_PPL = 22, + IPC_GLB_RESTORE_PPL = 23, + IPC_GLB_NOTIFY = 26, + IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ +}; + +enum skl_ipc_glb_reply { + IPC_GLB_REPLY_SUCCESS = 0, + + IPC_GLB_REPLY_UNKNOWN_MSG_TYPE = 1, + IPC_GLB_REPLY_ERROR_INVALID_PARAM = 2, + + IPC_GLB_REPLY_BUSY = 3, + IPC_GLB_REPLY_PENDING = 4, + IPC_GLB_REPLY_FAILURE = 5, + IPC_GLB_REPLY_INVALID_REQUEST = 6, + + IPC_GLB_REPLY_OUT_OF_MEMORY = 7, + IPC_GLB_REPLY_OUT_OF_MIPS = 8, + + IPC_GLB_REPLY_INVALID_RESOURCE_ID = 9, + IPC_GLB_REPLY_INVALID_RESOURCE_STATE = 10, + + IPC_GLB_REPLY_MOD_MGMT_ERROR = 100, + IPC_GLB_REPLY_MOD_LOAD_CL_FAILED = 101, + IPC_GLB_REPLY_MOD_LOAD_INVALID_HASH = 102, + + IPC_GLB_REPLY_MOD_UNLOAD_INST_EXIST = 103, + IPC_GLB_REPLY_MOD_NOT_INITIALIZED = 104, + + IPC_GLB_REPLY_INVALID_CONFIG_PARAM_ID = 120, + IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121, + IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140, + IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141, + + IPC_GLB_REPLY_PPL_NOT_INITIALIZED = 160, + IPC_GLB_REPLY_PPL_NOT_EXIST = 161, + IPC_GLB_REPLY_PPL_SAVE_FAILED = 162, + IPC_GLB_REPLY_PPL_RESTORE_FAILED = 163, + + IPC_MAX_STATUS = ((1<<IPC_IXC_STATUS_BITS)-1) +}; + +enum skl_ipc_notification_type { + IPC_GLB_NOTIFY_GLITCH = 0, + IPC_GLB_NOTIFY_OVERRUN = 1, + IPC_GLB_NOTIFY_UNDERRUN = 2, + IPC_GLB_NOTIFY_END_STREAM = 3, + IPC_GLB_NOTIFY_PHRASE_DETECTED = 4, + IPC_GLB_NOTIFY_RESOURCE_EVENT = 5, + IPC_GLB_NOTIFY_LOG_BUFFER_STATUS = 6, + IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7, + IPC_GLB_NOTIFY_FW_READY = 8 +}; + +/* Module Message Types */ +enum skl_ipc_module_msg { + IPC_MOD_INIT_INSTANCE = 0, + IPC_MOD_CONFIG_GET = 1, + IPC_MOD_CONFIG_SET = 2, + IPC_MOD_LARGE_CONFIG_GET = 3, + IPC_MOD_LARGE_CONFIG_SET = 4, + IPC_MOD_BIND = 5, + IPC_MOD_UNBIND = 6, + IPC_MOD_SET_DX = 7 +}; + +static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, + size_t tx_size) +{ + if (tx_size) + memcpy(msg->tx_data, tx_data, tx_size); +} + +static bool skl_ipc_is_dsp_busy(struct sst_dsp *dsp) +{ + u32 hipci; + + hipci = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCI); + return (hipci & SKL_ADSP_REG_HIPCI_BUSY); +} + +/* Lock to be held by caller */ +static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) +{ + struct skl_ipc_header *header = (struct skl_ipc_header *)(&msg->header); + + if (msg->tx_size) + sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); + sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCIE, + header->extension); + sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCI, + header->primary | SKL_ADSP_REG_HIPCI_BUSY); +} + +static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, + u64 ipc_header) +{ + struct ipc_message *msg = NULL; + struct skl_ipc_header *header = (struct skl_ipc_header *)(&ipc_header); + + if (list_empty(&ipc->rx_list)) { + dev_err(ipc->dev, "ipc: rx list is empty but received 0x%x\n", + header->primary); + goto out; + } + + msg = list_first_entry(&ipc->rx_list, struct ipc_message, list); + +out: + return msg; + +} + +static int skl_ipc_process_notification(struct sst_generic_ipc *ipc, + struct skl_ipc_header header) +{ + struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc); + + if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { + switch (IPC_GLB_NOTIFY_TYPE(header.primary)) { + + case IPC_GLB_NOTIFY_UNDERRUN: + dev_err(ipc->dev, "FW Underrun %x\n", header.primary); + break; + + case IPC_GLB_NOTIFY_RESOURCE_EVENT: + dev_err(ipc->dev, "MCPS Budget Violation: %x\n", + header.primary); + break; + + case IPC_GLB_NOTIFY_FW_READY: + skl->boot_complete = true; + wake_up(&skl->boot_wait); + break; + + default: + dev_err(ipc->dev, "ipc: Unhandled error msg=%x", + header.primary); + break; + } + } + + return 0; +} + +static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, + struct skl_ipc_header header) +{ + struct ipc_message *msg; + u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK; + u64 *ipc_header = (u64 *)(&header); + + msg = skl_ipc_reply_get_msg(ipc, *ipc_header); + if (msg == NULL) { + dev_dbg(ipc->dev, "ipc: rx list is empty\n"); + return; + } + + /* first process the header */ + switch (reply) { + case IPC_GLB_REPLY_SUCCESS: + dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary); + break; + + case IPC_GLB_REPLY_OUT_OF_MEMORY: + dev_err(ipc->dev, "ipc fw reply: %x: no memory\n", header.primary); + msg->errno = -ENOMEM; + break; + + case IPC_GLB_REPLY_BUSY: + dev_err(ipc->dev, "ipc fw reply: %x: Busy\n", header.primary); + msg->errno = -EBUSY; + break; + + default: + dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply); + msg->errno = -EINVAL; + break; + } + + if (reply != IPC_GLB_REPLY_SUCCESS) { + dev_err(ipc->dev, "ipc FW reply: reply=%d", reply); + dev_err(ipc->dev, "FW Error Code: %u\n", + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); + } + + list_del(&msg->list); + sst_ipc_tx_msg_reply_complete(ipc, msg); +} + +irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) +{ + struct sst_dsp *dsp = context; + struct skl_sst *skl = sst_dsp_get_thread_context(dsp); + struct sst_generic_ipc *ipc = &skl->ipc; + struct skl_ipc_header header = {0}; + u32 hipcie, hipct, hipcte; + int ipc_irq = 0; + + if (dsp->intr_status & SKL_ADSPIS_CL_DMA) + skl_cldma_process_intr(dsp); + + /* Here we handle IPC interrupts only */ + if (!(dsp->intr_status & SKL_ADSPIS_IPC)) + return IRQ_NONE; + + hipcie = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCIE); + hipct = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCT); + + /* reply message from DSP */ + if (hipcie & SKL_ADSP_REG_HIPCIE_DONE) { + sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, 0); + + /* clear DONE bit - tell DSP we have completed the operation */ + sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCIE, + SKL_ADSP_REG_HIPCIE_DONE, SKL_ADSP_REG_HIPCIE_DONE); + + ipc_irq = 1; + + /* unmask Done interrupt */ + sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE); + } + + /* New message from DSP */ + if (hipct & SKL_ADSP_REG_HIPCT_BUSY) { + hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE); + header.primary = hipct; + header.extension = hipcte; + dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x", + header.primary); + dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x", + header.extension); + + if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) { + /* Handle Immediate reply from DSP Core */ + skl_ipc_process_reply(ipc, header); + } else { + dev_dbg(dsp->dev, "IPC irq: Notification from firmware\n"); + skl_ipc_process_notification(ipc, header); + } + /* clear busy interrupt */ + sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCT, + SKL_ADSP_REG_HIPCT_BUSY, SKL_ADSP_REG_HIPCT_BUSY); + ipc_irq = 1; + } + + if (ipc_irq == 0) + return IRQ_NONE; + + skl_ipc_int_enable(dsp); + + /* continue to send any remaining messages... */ + queue_kthread_work(&ipc->kworker, &ipc->kwork); + + return IRQ_HANDLED; +} + +void skl_ipc_int_enable(struct sst_dsp *ctx) +{ + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_ADSPIC, + SKL_ADSPIC_IPC, SKL_ADSPIC_IPC); +} + +void skl_ipc_int_disable(struct sst_dsp *ctx) +{ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC, + SKL_ADSPIC_IPC, 0); +} + +void skl_ipc_op_int_enable(struct sst_dsp *ctx) +{ + /* enable IPC DONE interrupt */ + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE); + + /* Enable IPC BUSY interrupt */ + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY); +} + +bool skl_ipc_int_status(struct sst_dsp *ctx) +{ + return sst_dsp_shim_read_unlocked(ctx, + SKL_ADSP_REG_ADSPIS) & SKL_ADSPIS_IPC; +} + +int skl_ipc_init(struct device *dev, struct skl_sst *skl) +{ + struct sst_generic_ipc *ipc; + int err; + + ipc = &skl->ipc; + ipc->dsp = skl->dsp; + ipc->dev = dev; + + ipc->tx_data_max_size = SKL_ADSP_W1_SZ; + ipc->rx_data_max_size = SKL_ADSP_W0_UP_SZ; + + err = sst_ipc_init(ipc); + if (err) + return err; + + ipc->ops.tx_msg = skl_ipc_tx_msg; + ipc->ops.tx_data_copy = skl_ipc_tx_data_copy; + ipc->ops.is_dsp_busy = skl_ipc_is_dsp_busy; + + return 0; +} + +void skl_ipc_free(struct sst_generic_ipc *ipc) +{ + /* Disable IPC DONE interrupt */ + sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, 0); + + /* Disable IPC BUSY interrupt */ + sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_BUSY, 0); + + sst_ipc_fini(ipc); +} + +int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, + u16 ppl_mem_size, u8 ppl_type, u8 instance_id) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_CREATE_PPL); + header.primary |= IPC_INSTANCE_ID(instance_id); + header.primary |= IPC_PPL_TYPE(ppl_type); + header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: create pipeline fail, err: %d\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_create_pipeline); + +int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_DELETE_PPL); + header.primary |= IPC_INSTANCE_ID(instance_id); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: delete pipeline failed, err %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(skl_ipc_delete_pipeline); + +int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc, + u8 instance_id, enum skl_ipc_pipeline_state state) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_SET_PPL_STATE); + header.primary |= IPC_INSTANCE_ID(instance_id); + header.primary |= IPC_PPL_STATE(state); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: set pipeline state failed, err: %d\n", ret); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_set_pipeline_state); + +int +skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, u8 instance_id, int dma_id) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_SAVE_PPL); + header.primary |= IPC_INSTANCE_ID(instance_id); + + header.extension = IPC_DMA_ID(dma_id); + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: save pipeline failed, err: %d\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_save_pipeline); + +int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_RESTORE_PPL); + header.primary |= IPC_INSTANCE_ID(instance_id); + + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: restore pipeline failed, err: %d\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_restore_pipeline); + +int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, + u16 module_id, struct skl_ipc_dxstate_info *dx) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_DX); + header.primary |= IPC_MOD_INSTANCE_ID(instance_id); + header.primary |= IPC_MOD_ID(module_id); + + dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, + header.primary, header.extension); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, + dx, sizeof(dx), NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_set_dx); + +int skl_ipc_init_instance(struct sst_generic_ipc *ipc, + struct skl_ipc_init_instance_msg *msg, void *param_data) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + u32 *buffer = (u32 *)param_data; + /* param_block_size must be in dwords */ + u16 param_block_size = msg->param_data_size / sizeof(u32); + + print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE, + 16, 4, buffer, param_block_size, false); + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_INIT_INSTANCE); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_CORE_ID(msg->core_id); + header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id); + header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size); + + dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, + header.primary, header.extension); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, param_data, + msg->param_data_size, NULL, 0); + + if (ret < 0) { + dev_err(ipc->dev, "ipc: init instance failed\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_init_instance); + +int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, + struct skl_ipc_bind_unbind_msg *msg) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + u8 bind_unbind = msg->bind ? IPC_MOD_BIND : IPC_MOD_UNBIND; + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(bind_unbind); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_DST_MOD_ID(msg->dst_module_id); + header.extension |= IPC_DST_MOD_INSTANCE_ID(msg->dst_instance_id); + header.extension |= IPC_DST_QUEUE(msg->dst_queue); + header.extension |= IPC_SRC_QUEUE(msg->src_queue); + + dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary, + header.extension); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, "ipc: bind/unbind faileden"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind); + +int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, + struct skl_ipc_large_config_msg *msg, u32 *param) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret = 0; + size_t sz_remaining, tx_size, data_offset; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_SET); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); + header.extension |= IPC_FINAL_BLOCK(0); + header.extension |= IPC_INITIAL_BLOCK(1); + + sz_remaining = msg->param_data_size; + data_offset = 0; + while (sz_remaining != 0) { + tx_size = sz_remaining > SKL_ADSP_W1_SZ + ? SKL_ADSP_W1_SZ : sz_remaining; + if (tx_size == sz_remaining) + header.extension |= IPC_FINAL_BLOCK(1); + + dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__, + header.primary, header.extension); + dev_dbg(ipc->dev, "transmitting offset: %#x, size: %#x\n", + (unsigned)data_offset, (unsigned)tx_size); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, + ((char *)param) + data_offset, + tx_size, NULL, 0); + if (ret < 0) { + dev_err(ipc->dev, + "ipc: set large config fail, err: %d\n", ret); + return ret; + } + sz_remaining -= tx_size; + data_offset = msg->param_data_size - sz_remaining; + + /* clear the fields */ + header.extension &= IPC_INITIAL_BLOCK_CLEAR; + header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; + /* fill the fields */ + header.extension |= IPC_INITIAL_BLOCK(0); + header.extension |= IPC_DATA_OFFSET_SZ(data_offset); + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h new file mode 100644 index 0000000..9f5f672 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -0,0 +1,125 @@ +/* + * Intel SKL IPC Support + * + * Copyright (C) 2014-15, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __SKL_IPC_H +#define __SKL_IPC_H + +#include <linux/kthread.h> +#include <linux/irqreturn.h> +#include "../common/sst-ipc.h" + +struct sst_dsp; +struct skl_sst; +struct sst_generic_ipc; + +enum skl_ipc_pipeline_state { + PPL_INVALID_STATE = 0, + PPL_UNINITIALIZED = 1, + PPL_RESET = 2, + PPL_PAUSED = 3, + PPL_RUNNING = 4, + PPL_ERROR_STOP = 5, + PPL_SAVED = 6, + PPL_RESTORED = 7 +}; + +struct skl_ipc_dxstate_info { + u32 core_mask; + u32 dx_mask; +}; + +struct skl_ipc_header { + u32 primary; + u32 extension; +}; + +struct skl_sst { + struct device *dev; + struct sst_dsp *dsp; + + /* boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + + /* IPC messaging */ + struct sst_generic_ipc ipc; +}; + +struct skl_ipc_init_instance_msg { + u32 module_id; + u32 instance_id; + u16 param_data_size; + u8 ppl_instance_id; + u8 core_id; +}; + +struct skl_ipc_bind_unbind_msg { + u32 module_id; + u32 instance_id; + u32 dst_module_id; + u32 dst_instance_id; + u8 src_queue; + u8 dst_queue; + bool bind; +}; + +struct skl_ipc_large_config_msg { + u32 module_id; + u32 instance_id; + u32 large_param_id; + u32 param_data_size; +}; + +#define SKL_IPC_BOOT_MSECS 3000 + +#define SKL_IPC_D3_MASK 0 +#define SKL_IPC_D0_MASK 3 + +irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context); + +int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc, + u16 ppl_mem_size, u8 ppl_type, u8 instance_id); + +int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id); + +int skl_ipc_set_pipeline_state(struct sst_generic_ipc *sst_ipc, + u8 instance_id, enum skl_ipc_pipeline_state state); + +int skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, + u8 instance_id, int dma_id); + +int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id); + +int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc, + struct skl_ipc_init_instance_msg *msg, void *param_data); + +int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, + struct skl_ipc_bind_unbind_msg *msg); + +int skl_ipc_set_dx(struct sst_generic_ipc *ipc, + u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx); + +int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, + struct skl_ipc_large_config_msg *msg, u32 *param); + +void skl_ipc_int_enable(struct sst_dsp *dsp); +void skl_ipc_op_int_enable(struct sst_dsp *ctx); +void skl_ipc_int_disable(struct sst_dsp *dsp); + +bool skl_ipc_int_status(struct sst_dsp *dsp); +void skl_ipc_free(struct sst_generic_ipc *ipc); +int skl_ipc_init(struct device *dev, struct skl_sst *skl); + +#endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c new file mode 100644 index 0000000..c18ea51 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sst.c @@ -0,0 +1,280 @@ +/* + * skl-sst.c - HDA DSP library functions for SKL platform + * + * Copyright (C) 2014-15, Intel Corporation. + * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> + * Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-ipc.h" +#include "skl-sst-ipc.h" + +#define SKL_BASEFW_TIMEOUT 300 +#define SKL_INIT_TIMEOUT 1000 + +/* Intel HD Audio SRAM Window 0*/ +#define SKL_ADSP_SRAM0_BASE 0x8000 + +/* Firmware status window */ +#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE +#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) + +#define SKL_INSTANCE_ID 0 +#define SKL_BASE_FW_MODULE_ID 0 + +static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) +{ + u32 cur_sts; + + cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK; + + return (cur_sts == status); +} + +static int skl_transfer_firmware(struct sst_dsp *ctx, + const void *basefw, u32 base_fw_size) +{ + int ret = 0; + + ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size); + if (ret < 0) + return ret; + + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_FW_STATUS, + SKL_FW_STS_MASK, + SKL_FW_RFW_START, + SKL_BASEFW_TIMEOUT, + "Firmware boot"); + + ctx->cl_dev.ops.cl_stop_dma(ctx); + + return ret; +} + +static int skl_load_base_firmware(struct sst_dsp *ctx) +{ + int ret = 0, i; + const struct firmware *fw = NULL; + struct skl_sst *skl = ctx->thread_context; + u32 reg; + + ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + skl_dsp_disable_core(ctx); + return -EIO; + } + + /* enable Interrupt */ + skl_ipc_int_enable(ctx); + skl_ipc_op_int_enable(ctx); + + /* check ROM Status */ + for (i = SKL_INIT_TIMEOUT; i > 0; --i) { + if (skl_check_fw_status(ctx, SKL_FW_INIT)) { + dev_dbg(ctx->dev, + "ROM loaded, we can continue with FW loading\n"); + break; + } + mdelay(1); + } + if (!i) { + reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS); + dev_err(ctx->dev, + "Timeout waiting for ROM init done, reg:0x%x\n", reg); + ret = -EIO; + goto skl_load_base_firmware_failed; + } + + ret = skl_transfer_firmware(ctx, fw->data, fw->size); + if (ret < 0) { + dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); + goto skl_load_base_firmware_failed; + } else { + ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, + msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); + ret = -EIO; + goto skl_load_base_firmware_failed; + } + + dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); + skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + } + release_firmware(fw); + + return 0; + +skl_load_base_firmware_failed: + skl_dsp_disable_core(ctx); + release_firmware(fw); + return ret; +} + +static int skl_set_dsp_D0(struct sst_dsp *ctx) +{ + int ret; + + ret = skl_load_base_firmware(ctx); + if (ret < 0) { + dev_err(ctx->dev, "unable to load firmware\n"); + return ret; + } + + skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + + return ret; +} + +static int skl_set_dsp_D3(struct sst_dsp *ctx) +{ + int ret; + struct skl_ipc_dxstate_info dx; + struct skl_sst *skl = ctx->thread_context; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + mutex_lock(&ctx->mutex); + if (!is_skl_dsp_running(ctx)) { + mutex_unlock(&ctx->mutex); + return 0; + } + mutex_unlock(&ctx->mutex); + + dx.core_mask = SKL_DSP_CORE0_MASK; + dx.dx_mask = SKL_IPC_D3_MASK; + ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D3 state\n"); + return ret; + } + + ret = skl_dsp_disable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret); + ret = -EIO; + } + skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + + return ret; +} + +static unsigned int skl_get_errorcode(struct sst_dsp *ctx) +{ + return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); +} + +static struct skl_dsp_fw_ops skl_fw_ops = { + .set_state_D0 = skl_set_dsp_D0, + .set_state_D3 = skl_set_dsp_D3, + .load_fw = skl_load_base_firmware, + .get_fw_errcode = skl_get_errorcode, +}; + +static struct sst_ops skl_ops = { + .irq_handler = skl_dsp_sst_interrupt, + .write = sst_shim32_write, + .read = sst_shim32_read, + .ram_read = sst_memcpy_fromio_32, + .ram_write = sst_memcpy_toio_32, + .free = skl_dsp_free, +}; + +static struct sst_dsp_device skl_dev = { + .thread = skl_dsp_irq_thread_handler, + .ops = &skl_ops, +}; + +int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, + struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) +{ + struct skl_sst *skl; + struct sst_dsp *sst; + int ret; + + skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL); + if (skl == NULL) + return -ENOMEM; + + skl->dev = dev; + skl_dev.thread_context = skl; + + skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); + if (!skl->dsp) { + dev_err(skl->dev, "%s: no device\n", __func__); + return -ENODEV; + } + + sst = skl->dsp; + + sst->addr.lpe = mmio_base; + sst->addr.shim = mmio_base; + sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), + SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); + + sst->dsp_ops = dsp_ops; + sst->fw_ops = skl_fw_ops; + + ret = skl_ipc_init(dev, skl); + if (ret) + return ret; + + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + ret = skl_dsp_boot(sst); + if (ret < 0) { + dev_err(skl->dev, "Boot dsp core failed ret: %d", ret); + goto free_ipc; + } + + ret = skl_cldma_prepare(sst); + if (ret < 0) { + dev_err(dev, "CL dma prepare failed : %d", ret); + goto free_ipc; + } + + + ret = sst->fw_ops.load_fw(sst); + if (ret < 0) { + dev_err(dev, "Load base fw failed : %d", ret); + return ret; + } + + if (dsp) + *dsp = skl; + + return 0; + +free_ipc: + skl_ipc_free(&skl->ipc); + return ret; +} +EXPORT_SYMBOL_GPL(skl_sst_dsp_init); + +void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) +{ + skl_ipc_free(&ctx->ipc); + ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); + ctx->dsp->ops->free(ctx->dsp); +} +EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Skylake IPC driver"); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h new file mode 100644 index 0000000..8c7767b --- /dev/null +++ b/sound/soc/intel/skylake/skl-topology.h @@ -0,0 +1,286 @@ +/* + * skl_topology.h - Intel HDA Platform topology header file + * + * Copyright (C) 2014-15 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SKL_TOPOLOGY_H__ +#define __SKL_TOPOLOGY_H__ + +#include <linux/types.h> + +#include <sound/hdaudio_ext.h> +#include <sound/soc.h> +#include "skl.h" +#include "skl-tplg-interface.h" + +#define BITS_PER_BYTE 8 +#define MAX_TS_GROUPS 8 +#define MAX_DMIC_TS_GROUPS 4 +#define MAX_FIXED_DMIC_PARAMS_SIZE 727 + +/* Maximum number of coefficients up down mixer module */ +#define UP_DOWN_MIXER_MAX_COEFF 6 + +enum skl_channel_index { + SKL_CHANNEL_LEFT = 0, + SKL_CHANNEL_RIGHT = 1, + SKL_CHANNEL_CENTER = 2, + SKL_CHANNEL_LEFT_SURROUND = 3, + SKL_CHANNEL_CENTER_SURROUND = 3, + SKL_CHANNEL_RIGHT_SURROUND = 4, + SKL_CHANNEL_LFE = 7, + SKL_CHANNEL_INVALID = 0xF, +}; + +enum skl_bitdepth { + SKL_DEPTH_8BIT = 8, + SKL_DEPTH_16BIT = 16, + SKL_DEPTH_24BIT = 24, + SKL_DEPTH_32BIT = 32, + SKL_DEPTH_INVALID +}; + +enum skl_interleaving { + /* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */ + SKL_INTERLEAVING_PER_CHANNEL = 0, + /* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */ + SKL_INTERLEAVING_PER_SAMPLE = 1, +}; + +enum skl_s_freq { + SKL_FS_8000 = 8000, + SKL_FS_11025 = 11025, + SKL_FS_12000 = 12000, + SKL_FS_16000 = 16000, + SKL_FS_22050 = 22050, + SKL_FS_24000 = 24000, + SKL_FS_32000 = 32000, + SKL_FS_44100 = 44100, + SKL_FS_48000 = 48000, + SKL_FS_64000 = 64000, + SKL_FS_88200 = 88200, + SKL_FS_96000 = 96000, + SKL_FS_128000 = 128000, + SKL_FS_176400 = 176400, + SKL_FS_192000 = 192000, + SKL_FS_INVALID +}; + +enum skl_widget_type { + SKL_WIDGET_VMIXER = 1, + SKL_WIDGET_MIXER = 2, + SKL_WIDGET_PGA = 3, + SKL_WIDGET_MUX = 4 +}; + +struct skl_audio_data_format { + enum skl_s_freq s_freq; + enum skl_bitdepth bit_depth; + u32 channel_map; + enum skl_ch_cfg ch_cfg; + enum skl_interleaving interleaving; + u8 number_of_channels; + u8 valid_bit_depth; + u8 sample_type; + u8 reserved[1]; +} __packed; + +struct skl_base_cfg { + u32 cps; + u32 ibs; + u32 obs; + u32 is_pages; + struct skl_audio_data_format audio_fmt; +}; + +struct skl_cpr_gtw_cfg { + u32 node_id; + u32 dma_buffer_size; + u32 config_length; + /* not mandatory; required only for DMIC/I2S */ + u32 config_data[1]; +} __packed; + +struct skl_cpr_cfg { + struct skl_base_cfg base_cfg; + struct skl_audio_data_format out_fmt; + u32 cpr_feature_mask; + struct skl_cpr_gtw_cfg gtw_cfg; +} __packed; + + +struct skl_src_module_cfg { + struct skl_base_cfg base_cfg; + enum skl_s_freq src_cfg; +} __packed; + +struct skl_up_down_mixer_cfg { + struct skl_base_cfg base_cfg; + enum skl_ch_cfg out_ch_cfg; + /* This should be set to 1 if user coefficients are required */ + u32 coeff_sel; + /* Pass the user coeff in this array */ + s32 coeff[UP_DOWN_MIXER_MAX_COEFF]; +} __packed; + +enum skl_dma_type { + SKL_DMA_HDA_HOST_OUTPUT_CLASS = 0, + SKL_DMA_HDA_HOST_INPUT_CLASS = 1, + SKL_DMA_HDA_HOST_INOUT_CLASS = 2, + SKL_DMA_HDA_LINK_OUTPUT_CLASS = 8, + SKL_DMA_HDA_LINK_INPUT_CLASS = 9, + SKL_DMA_HDA_LINK_INOUT_CLASS = 0xA, + SKL_DMA_DMIC_LINK_INPUT_CLASS = 0xB, + SKL_DMA_I2S_LINK_OUTPUT_CLASS = 0xC, + SKL_DMA_I2S_LINK_INPUT_CLASS = 0xD, +}; + +union skl_ssp_dma_node { + u8 val; + struct { + u8 dual_mono:1; + u8 time_slot:3; + u8 i2s_instance:4; + } dma_node; +}; + +union skl_connector_node_id { + u32 val; + struct { + u32 vindex:8; + u32 dma_type:4; + u32 rsvd:20; + } node; +}; + +struct skl_module_fmt { + u32 channels; + u32 s_freq; + u32 bit_depth; + u32 valid_bit_depth; + u32 ch_cfg; +}; + +struct skl_module_inst_id { + u32 module_id; + u32 instance_id; +}; + +struct skl_module_pin { + struct skl_module_inst_id id; + u8 pin_index; + bool is_dynamic; + bool in_use; +}; + +struct skl_specific_cfg { + u32 caps_size; + u32 *caps; +}; + +enum skl_pipe_state { + SKL_PIPE_INVALID = 0, + SKL_PIPE_CREATED = 1, + SKL_PIPE_PAUSED = 2, + SKL_PIPE_STARTED = 3 +}; + +struct skl_pipe_module { + struct snd_soc_dapm_widget *w; + struct list_head node; +}; + +struct skl_pipe_params { + u8 host_dma_id; + u8 link_dma_id; + u32 ch; + u32 s_freq; + u32 s_fmt; + u8 linktype; + int stream; +}; + +struct skl_pipe { + u8 ppl_id; + u8 pipe_priority; + u16 conn_type; + u32 memory_pages; + struct skl_pipe_params *p_params; + enum skl_pipe_state state; + struct list_head w_list; +}; + +enum skl_module_state { + SKL_MODULE_UNINIT = 0, + SKL_MODULE_INIT_DONE = 1, + SKL_MODULE_LOADED = 2, + SKL_MODULE_UNLOADED = 3, + SKL_MODULE_BIND_DONE = 4 +}; + +struct skl_module_cfg { + struct skl_module_inst_id id; + struct skl_module_fmt in_fmt; + struct skl_module_fmt out_fmt; + u8 max_in_queue; + u8 max_out_queue; + u8 in_queue_mask; + u8 out_queue_mask; + u8 in_queue; + u8 out_queue; + u32 mcps; + u32 ibs; + u32 obs; + u8 is_loadable; + u8 core_id; + u8 dev_type; + u8 dma_id; + u8 time_slot; + u32 params_fixup; + u32 converter; + u32 vbus_id; + struct skl_module_pin *m_in_pin; + struct skl_module_pin *m_out_pin; + enum skl_module_type m_type; + enum skl_hw_conn_type hw_conn_type; + enum skl_module_state m_state; + struct skl_pipe *pipe; + struct skl_specific_cfg formats_config; +}; + +int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); + +int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); + +int skl_pause_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); + +int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); + +int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); + +int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config, + char *param); + +int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg + *src_module, struct skl_module_cfg *dst_module); + +int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg + *src_module, struct skl_module_cfg *dst_module); + +enum skl_bitdepth skl_get_bit_depth(int params); +#endif diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h new file mode 100644 index 0000000..a506898 --- /dev/null +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -0,0 +1,88 @@ +/* + * skl-tplg-interface.h - Intel DSP FW private data interface + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.com> + * Nilofer, Samreen <samreen.nilofer@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __HDA_TPLG_INTERFACE_H__ +#define __HDA_TPLG_INTERFACE_H__ + +/** + * enum skl_ch_cfg - channel configuration + * + * @SKL_CH_CFG_MONO: One channel only + * @SKL_CH_CFG_STEREO: L & R + * @SKL_CH_CFG_2_1: L, R & LFE + * @SKL_CH_CFG_3_0: L, C & R + * @SKL_CH_CFG_3_1: L, C, R & LFE + * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs + * @SKL_CH_CFG_4_0: L, C, R & Cs + * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs + * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE + * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two + * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] + * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] + * @SKL_CH_CFG_INVALID: Invalid + */ +enum skl_ch_cfg { + SKL_CH_CFG_MONO = 0, + SKL_CH_CFG_STEREO = 1, + SKL_CH_CFG_2_1 = 2, + SKL_CH_CFG_3_0 = 3, + SKL_CH_CFG_3_1 = 4, + SKL_CH_CFG_QUATRO = 5, + SKL_CH_CFG_4_0 = 6, + SKL_CH_CFG_5_0 = 7, + SKL_CH_CFG_5_1 = 8, + SKL_CH_CFG_DUAL_MONO = 9, + SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, + SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, + SKL_CH_CFG_INVALID +}; + +enum skl_module_type { + SKL_MODULE_TYPE_MIXER = 0, + SKL_MODULE_TYPE_COPIER, + SKL_MODULE_TYPE_UPDWMIX, + SKL_MODULE_TYPE_SRCINT +}; + +enum skl_core_affinity { + SKL_AFFINITY_CORE_0 = 0, + SKL_AFFINITY_CORE_1, + SKL_AFFINITY_CORE_MAX +}; + +enum skl_pipe_conn_type { + SKL_PIPE_CONN_TYPE_NONE = 0, + SKL_PIPE_CONN_TYPE_FE, + SKL_PIPE_CONN_TYPE_BE +}; + +enum skl_hw_conn_type { + SKL_CONN_NONE = 0, + SKL_CONN_SOURCE = 1, + SKL_CONN_SINK = 2 +}; + +enum skl_dev_type { + SKL_DEVICE_BT = 0x0, + SKL_DEVICE_DMIC = 0x1, + SKL_DEVICE_I2S = 0x2, + SKL_DEVICE_SLIMBUS = 0x3, + SKL_DEVICE_HDALINK = 0x4, + SKL_DEVICE_NONE +}; +#endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c new file mode 100644 index 0000000..348d094 --- /dev/null +++ b/sound/soc/intel/skylake/skl.c @@ -0,0 +1,536 @@ +/* + * skl.c - Implementation of ASoC Intel SKL HD Audio driver + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.com> + * + * Derived mostly from Intel HDA driver with following copyrights: + * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> + * PeiSen Hou <pshou@realtek.com.tw> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include "skl.h" + +/* + * initialize the PCI registers + */ +static void skl_update_pci_byte(struct pci_dev *pci, unsigned int reg, + unsigned char mask, unsigned char val) +{ + unsigned char data; + + pci_read_config_byte(pci, reg, &data); + data &= ~mask; + data |= (val & mask); + pci_write_config_byte(pci, reg, data); +} + +static void skl_init_pci(struct skl *skl) +{ + struct hdac_ext_bus *ebus = &skl->ebus; + + /* + * Clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio + * codecs. + * The PCI register TCSEL is defined in the Intel manuals. + */ + dev_dbg(ebus_to_hbus(ebus)->dev, "Clearing TCSEL\n"); + skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0); +} + +/* called from IRQ */ +static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) +{ + snd_pcm_period_elapsed(hstr->substream); +} + +static irqreturn_t skl_interrupt(int irq, void *dev_id) +{ + struct hdac_ext_bus *ebus = dev_id; + struct hdac_bus *bus = ebus_to_hbus(ebus); + u32 status; + + if (!pm_runtime_active(bus->dev)) + return IRQ_NONE; + + spin_lock(&bus->reg_lock); + + status = snd_hdac_chip_readl(bus, INTSTS); + if (status == 0 || status == 0xffffffff) { + spin_unlock(&bus->reg_lock); + return IRQ_NONE; + } + + /* clear rirb int */ + status = snd_hdac_chip_readb(bus, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(bus); + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + } + + spin_unlock(&bus->reg_lock); + + return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +static irqreturn_t skl_threaded_handler(int irq, void *dev_id) +{ + struct hdac_ext_bus *ebus = dev_id; + struct hdac_bus *bus = ebus_to_hbus(ebus); + u32 status; + + status = snd_hdac_chip_readl(bus, INTSTS); + + snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update); + + return IRQ_HANDLED; +} + +static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + int ret; + + ret = request_threaded_irq(skl->pci->irq, skl_interrupt, + skl_threaded_handler, + IRQF_SHARED, + KBUILD_MODNAME, ebus); + if (ret) { + dev_err(bus->dev, + "unable to grab IRQ %d, disabling device\n", + skl->pci->irq); + return ret; + } + + bus->irq = skl->pci->irq; + pci_intx(skl->pci, 1); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/* + * power management + */ +static int skl_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + snd_hdac_bus_stop_chip(bus); + snd_hdac_bus_enter_link_reset(bus); + + return 0; +} + +static int skl_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *hda = ebus_to_skl(ebus); + + skl_init_pci(hda); + + snd_hdac_bus_init_chip(bus, 1); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int skl_runtime_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + dev_dbg(bus->dev, "in %s\n", __func__); + + /* enable controller wake up event */ + snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + + snd_hdac_bus_stop_chip(bus); + snd_hdac_bus_enter_link_reset(bus); + + return 0; +} + +static int skl_runtime_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *hda = ebus_to_skl(ebus); + int status; + + dev_dbg(bus->dev, "in %s\n", __func__); + + /* Read STATESTS before controller reset */ + status = snd_hdac_chip_readw(bus, STATESTS); + + skl_init_pci(hda); + snd_hdac_bus_init_chip(bus, true); + /* disable controller Wake Up event */ + snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops skl_pm = { + SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume) + SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL) +}; + +/* + * destructor + */ +static int skl_free(struct hdac_ext_bus *ebus) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + skl->init_failed = 1; /* to be sure */ + + snd_hdac_ext_stop_streams(ebus); + + if (bus->irq >= 0) + free_irq(bus->irq, (void *)bus); + if (bus->remap_addr) + iounmap(bus->remap_addr); + + snd_hdac_bus_free_stream_pages(bus); + snd_hdac_stream_free_all(ebus); + snd_hdac_link_free_all(ebus); + pci_release_regions(skl->pci); + pci_disable_device(skl->pci); + + snd_hdac_ext_bus_exit(ebus); + + return 0; +} + +static int skl_dmic_device_register(struct skl *skl) +{ + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct platform_device *pdev; + int ret; + + /* SKL has one dmic port, so allocate dmic device for this */ + pdev = platform_device_alloc("dmic-codec", -1); + if (!pdev) { + dev_err(bus->dev, "failed to allocate dmic device\n"); + return -ENOMEM; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(bus->dev, "failed to add dmic device: %d\n", ret); + platform_device_put(pdev); + return ret; + } + skl->dmic_dev = pdev; + + return 0; +} + +static void skl_dmic_device_unregister(struct skl *skl) +{ + if (skl->dmic_dev) + platform_device_unregister(skl->dmic_dev); +} + +/* + * Probe the given codec address + */ +static int probe_codec(struct hdac_ext_bus *ebus, int addr) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + unsigned int res; + + mutex_lock(&bus->cmd_mutex); + snd_hdac_bus_send_cmd(bus, cmd); + snd_hdac_bus_get_response(bus, addr, &res); + mutex_unlock(&bus->cmd_mutex); + if (res == -1) + return -EIO; + dev_dbg(bus->dev, "codec #%d probed OK\n", addr); + + return snd_hdac_ext_bus_device_init(ebus, addr); +} + +/* Codec initialization */ +static int skl_codec_create(struct hdac_ext_bus *ebus) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + int c, max_slots; + + max_slots = HDA_MAX_CODECS; + + /* First try to probe all given codec slots */ + for (c = 0; c < max_slots; c++) { + if ((bus->codec_mask & (1 << c))) { + if (probe_codec(ebus, c) < 0) { + /* + * Some BIOSen give you wrong codec addresses + * that don't exist + */ + dev_warn(bus->dev, + "Codec #%d probe error; disabling it...\n", c); + bus->codec_mask &= ~(1 << c); + /* + * More badly, accessing to a non-existing + * codec often screws up the controller bus, + * and disturbs the further communications. + * Thus if an error occurs during probing, + * better to reset the controller bus to get + * back to the sanity state. + */ + snd_hdac_bus_stop_chip(bus); + snd_hdac_bus_init_chip(bus, true); + } + } + } + + return 0; +} + +static const struct hdac_bus_ops bus_core_ops = { + .command = snd_hdac_bus_send_cmd, + .get_response = snd_hdac_bus_get_response, +}; + +/* + * constructor + */ +static int skl_create(struct pci_dev *pci, + const struct hdac_io_ops *io_ops, + struct skl **rskl) +{ + struct skl *skl; + struct hdac_ext_bus *ebus; + + int err; + + *rskl = NULL; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + skl = devm_kzalloc(&pci->dev, sizeof(*skl), GFP_KERNEL); + if (!skl) { + pci_disable_device(pci); + return -ENOMEM; + } + ebus = &skl->ebus; + snd_hdac_ext_bus_init(ebus, &pci->dev, &bus_core_ops, io_ops); + ebus->bus.use_posbuf = 1; + skl->pci = pci; + + ebus->bus.bdl_pos_adj = 0; + + *rskl = skl; + + return 0; +} + +static int skl_first_init(struct hdac_ext_bus *ebus) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct pci_dev *pci = skl->pci; + int err; + unsigned short gcap; + int cp_streams, pb_streams, start_idx; + + err = pci_request_regions(pci, "Skylake HD audio"); + if (err < 0) + return err; + + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (bus->remap_addr == NULL) { + dev_err(bus->dev, "ioremap error\n"); + return -ENXIO; + } + + snd_hdac_ext_bus_parse_capabilities(ebus); + + if (skl_acquire_irq(ebus, 0) < 0) + return -EBUSY; + + pci_set_master(pci); + synchronize_irq(bus->irq); + + gcap = snd_hdac_chip_readw(bus, GCAP); + dev_dbg(bus->dev, "chipset global capabilities = 0x%x\n", gcap); + + /* allow 64bit DMA address if supported by H/W */ + if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) { + dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64)); + } else { + dma_set_mask(bus->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32)); + } + + /* read number of streams from GCAP register */ + cp_streams = (gcap >> 8) & 0x0f; + pb_streams = (gcap >> 12) & 0x0f; + + if (!pb_streams && !cp_streams) + return -EIO; + + ebus->num_streams = cp_streams + pb_streams; + + /* initialize streams */ + snd_hdac_ext_stream_init_all + (ebus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); + start_idx = cp_streams; + snd_hdac_ext_stream_init_all + (ebus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); + + err = snd_hdac_bus_alloc_stream_pages(bus); + if (err < 0) + return err; + + /* initialize chip */ + skl_init_pci(skl); + + snd_hdac_bus_init_chip(bus, true); + + /* codec detection */ + if (!bus->codec_mask) { + dev_err(bus->dev, "no codecs found!\n"); + return -ENODEV; + } + + return 0; +} + +static int skl_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct skl *skl; + struct hdac_ext_bus *ebus = NULL; + struct hdac_bus *bus = NULL; + int err; + + /* we use ext core ops, so provide NULL for ops here */ + err = skl_create(pci, NULL, &skl); + if (err < 0) + return err; + + ebus = &skl->ebus; + bus = ebus_to_hbus(ebus); + + err = skl_first_init(ebus); + if (err < 0) + goto out_free; + + pci_set_drvdata(skl->pci, ebus); + + /* check if dsp is there */ + if (ebus->ppcap) { + /* TODO register with dsp IPC */ + dev_dbg(bus->dev, "Register dsp\n"); + } + + if (ebus->mlcap) + snd_hdac_ext_bus_get_ml_capabilities(ebus); + + /* create device for soc dmic */ + err = skl_dmic_device_register(skl); + if (err < 0) + goto out_free; + + /* register platform dai and controls */ + err = skl_platform_register(bus->dev); + if (err < 0) + goto out_dmic_free; + + /* create codec instances */ + err = skl_codec_create(ebus); + if (err < 0) + goto out_unregister; + + /*configure PM */ + pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY); + pm_runtime_use_autosuspend(bus->dev); + pm_runtime_put_noidle(bus->dev); + pm_runtime_allow(bus->dev); + + return 0; + +out_unregister: + skl_platform_unregister(bus->dev); +out_dmic_free: + skl_dmic_device_unregister(skl); +out_free: + skl->init_failed = 1; + skl_free(ebus); + + return err; +} + +static void skl_remove(struct pci_dev *pci) +{ + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct skl *skl = ebus_to_skl(ebus); + + if (pci_dev_run_wake(pci)) + pm_runtime_get_noresume(&pci->dev); + pci_dev_put(pci); + skl_platform_unregister(&pci->dev); + skl_dmic_device_unregister(skl); + skl_free(ebus); + dev_set_drvdata(&pci->dev, NULL); +} + +/* PCI IDs */ +static const struct pci_device_id skl_ids[] = { + /* Sunrise Point-LP */ + { PCI_DEVICE(0x8086, 0x9d70), 0}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, skl_ids); + +/* pci_driver definition */ +static struct pci_driver skl_driver = { + .name = KBUILD_MODNAME, + .id_table = skl_ids, + .probe = skl_probe, + .remove = skl_remove, + .driver = { + .pm = &skl_pm, + }, +}; +module_pci_driver(skl_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Skylake ASoC HDA driver"); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h new file mode 100644 index 0000000..f7fdbb0 --- /dev/null +++ b/sound/soc/intel/skylake/skl.h @@ -0,0 +1,84 @@ +/* + * skl.h - HD Audio skylake defintions. + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SOUND_SOC_SKL_H +#define __SOUND_SOC_SKL_H + +#include <sound/hda_register.h> +#include <sound/hdaudio_ext.h> +#include "skl-nhlt.h" + +#define SKL_SUSPEND_DELAY 2000 + +/* Vendor Specific Registers */ +#define AZX_REG_VS_EM1 0x1000 +#define AZX_REG_VS_INRC 0x1004 +#define AZX_REG_VS_OUTRC 0x1008 +#define AZX_REG_VS_FIFOTRK 0x100C +#define AZX_REG_VS_FIFOTRK2 0x1010 +#define AZX_REG_VS_EM2 0x1030 +#define AZX_REG_VS_EM3L 0x1038 +#define AZX_REG_VS_EM3U 0x103C +#define AZX_REG_VS_EM4L 0x1040 +#define AZX_REG_VS_EM4U 0x1044 +#define AZX_REG_VS_LTRC 0x1048 +#define AZX_REG_VS_D0I3C 0x104A +#define AZX_REG_VS_PCE 0x104B +#define AZX_REG_VS_L2MAGC 0x1050 +#define AZX_REG_VS_L2LAHPT 0x1054 +#define AZX_REG_VS_SDXDPIB_XBASE 0x1084 +#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20 +#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 +#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 + +struct skl { + struct hdac_ext_bus ebus; + struct pci_dev *pci; + + unsigned int init_failed:1; /* delayed init failed */ + struct platform_device *dmic_dev; + + void __iomem *nhlt; /* nhlt ptr */ + struct skl_sst *skl_sst; /* sst skl ctx */ +}; + +#define skl_to_ebus(s) (&(s)->ebus) +#define ebus_to_skl(sbus) \ + container_of(sbus, struct skl, sbus) + +/* to pass dai dma data */ +struct skl_dma_params { + u32 format; + u8 stream_tag; +}; + +int skl_platform_unregister(struct device *dev); +int skl_platform_register(struct device *dev); + +void __iomem *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(void __iomem *addr); +struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, + u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); + +int skl_init_dsp(struct skl *skl); +void skl_free_dsp(struct skl *skl); +int skl_suspend_dsp(struct skl *skl); +int skl_resume_dsp(struct skl *skl); +#endif /* __SOUND_SOC_SKL_H */ diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 4cf2245..dbfdfe9 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -148,10 +148,14 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) dram = mv_mbus_dram_info(); addr = substream->dma_buffer.addr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (priv->substream_play) + return -EBUSY; priv->substream_play = substream; kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_PLAYBACK_WIN, addr, dram); } else { + if (priv->substream_rec) + return -EBUSY; priv->substream_rec = substream; kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_RECORD_WIN, addr, dram); diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c index 2d2536a..684e8a7 100644 --- a/sound/soc/mediatek/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -136,6 +136,7 @@ static struct snd_soc_dai_link mt8173_max98090_dais[] = { static struct snd_soc_card mt8173_max98090_card = { .name = "mt8173-max98090", + .owner = THIS_MODULE, .dai_link = mt8173_max98090_dais, .num_links = ARRAY_SIZE(mt8173_max98090_dais), .controls = mt8173_max98090_controls, @@ -202,7 +203,6 @@ MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); static struct platform_driver mt8173_max98090_driver = { .driver = { .name = "mt8173-max98090", - .owner = THIS_MODULE, .of_match_table = mt8173_max98090_dt_match, #ifdef CONFIG_PM .pm = &snd_soc_pm_ops, diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 6f52eca..86cf975 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -191,6 +191,7 @@ static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { static struct snd_soc_card mt8173_rt5650_rt5676_card = { .name = "mtk-rt5650-rt5676", + .owner = THIS_MODULE, .dai_link = mt8173_rt5650_rt5676_dais, .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), .codec_conf = mt8173_rt5650_rt5676_codec_conf, @@ -269,7 +270,6 @@ MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); static struct platform_driver mt8173_rt5650_rt5676_driver = { .driver = { .name = "mtk-rt5650-rt5676", - .owner = THIS_MODULE, .of_match_table = mt8173_rt5650_rt5676_dt_match, #ifdef CONFIG_PM .pm = &snd_soc_pm_ops, diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h index a88b175..cc4393c 100644 --- a/sound/soc/mediatek/mtk-afe-common.h +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -98,12 +98,4 @@ struct mtk_afe_memif { const struct mtk_afe_irq_data *irqdata; }; -struct mtk_afe { - /* address for ioremap audio hardware register */ - void __iomem *base_addr; - struct device *dev; - struct regmap *regmap; - struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; - struct clk *clocks[MTK_CLK_NUM]; -}; #endif diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index 9863da7..d190fe0 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -45,18 +45,21 @@ /* Memory interface */ #define AFE_DL1_BASE 0x0040 #define AFE_DL1_CUR 0x0044 +#define AFE_DL1_END 0x0048 #define AFE_DL2_BASE 0x0050 #define AFE_DL2_CUR 0x0054 #define AFE_AWB_BASE 0x0070 #define AFE_AWB_CUR 0x007c #define AFE_VUL_BASE 0x0080 #define AFE_VUL_CUR 0x008c +#define AFE_VUL_END 0x0088 #define AFE_DAI_BASE 0x0090 #define AFE_DAI_CUR 0x009c #define AFE_MOD_PCM_BASE 0x0330 #define AFE_MOD_PCM_CUR 0x033c #define AFE_HDMI_OUT_BASE 0x0374 #define AFE_HDMI_OUT_CUR 0x0378 +#define AFE_HDMI_OUT_END 0x037c #define AFE_ADDA2_TOP_CON0 0x0600 @@ -127,6 +130,34 @@ enum afe_tdm_ch_start { AFE_TDM_CH_ZERO, }; +static const unsigned int mtk_afe_backup_list[] = { + AUDIO_TOP_CON0, + AFE_CONN1, + AFE_CONN2, + AFE_CONN7, + AFE_CONN8, + AFE_DAC_CON1, + AFE_DL1_BASE, + AFE_DL1_END, + AFE_VUL_BASE, + AFE_VUL_END, + AFE_HDMI_OUT_BASE, + AFE_HDMI_OUT_END, + AFE_HDMI_CONN0, + AFE_DAC_CON0, +}; + +struct mtk_afe { + /* address for ioremap audio hardware register */ + void __iomem *base_addr; + struct device *dev; + struct regmap *regmap; + struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; + struct clk *clocks[MTK_CLK_NUM]; + unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)]; + bool suspended; +}; + static const struct snd_pcm_hardware mtk_afe_hardware = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID), @@ -722,11 +753,53 @@ static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { }; +static int mtk_afe_runtime_suspend(struct device *dev); +static int mtk_afe_runtime_resume(struct device *dev); + +static int mtk_afe_dai_suspend(struct snd_soc_dai *dai) +{ + struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai); + int i; + + dev_dbg(afe->dev, "%s\n", __func__); + if (pm_runtime_status_suspended(afe->dev) || afe->suspended) + return 0; + + for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++) + regmap_read(afe->regmap, mtk_afe_backup_list[i], + &afe->backup_regs[i]); + + afe->suspended = true; + mtk_afe_runtime_suspend(afe->dev); + return 0; +} + +static int mtk_afe_dai_resume(struct snd_soc_dai *dai) +{ + struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai); + int i = 0; + + dev_dbg(afe->dev, "%s\n", __func__); + if (pm_runtime_status_suspended(afe->dev) || !afe->suspended) + return 0; + + mtk_afe_runtime_resume(afe->dev); + + for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++) + regmap_write(afe->regmap, mtk_afe_backup_list[i], + afe->backup_regs[i]); + + afe->suspended = false; + return 0; +} + static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { /* FE DAIs: memory intefaces to CPU */ { .name = "DL1", /* downlink 1 */ .id = MTK_AFE_MEMIF_DL1, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, .playback = { .stream_name = "DL1", .channels_min = 1, @@ -738,6 +811,8 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { }, { .name = "VUL", /* voice uplink */ .id = MTK_AFE_MEMIF_VUL, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, .capture = { .stream_name = "VUL", .channels_min = 1, @@ -774,6 +849,8 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { { .name = "HDMI", .id = MTK_AFE_MEMIF_HDMI, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, .playback = { .stream_name = "HDMI", .channels_min = 2, @@ -820,10 +897,6 @@ static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { }; static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { - /* Backend DAIs */ - SND_SOC_DAPM_AIF_IN("I2S Capture", NULL, 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("I2S Playback", NULL, 0, SND_SOC_NOPM, 0, 0), - /* inter-connections */ SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -855,11 +928,6 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { { "O10", "I18 Switch", "I18" }, }; -static const struct snd_soc_dapm_widget mtk_afe_hdmi_widgets[] = { - /* Backend DAIs */ - SND_SOC_DAPM_AIF_OUT("HDMIO Playback", NULL, 0, SND_SOC_NOPM, 0, 0), -}; - static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { {"HDMIO Playback", NULL, "HDMI"}, }; @@ -874,8 +942,6 @@ static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { .name = "mtk-afe-hdmi-dai", - .dapm_widgets = mtk_afe_hdmi_widgets, - .num_dapm_widgets = ARRAY_SIZE(mtk_afe_hdmi_widgets), .dapm_routes = mtk_afe_hdmi_routes, .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), }; @@ -1220,7 +1286,6 @@ static const struct dev_pm_ops mtk_afe_pm_ops = { static struct platform_driver mtk_afe_pcm_driver = { .driver = { .name = "mtk-afe-pcm", - .owner = THIS_MODULE, .of_match_table = mtk_afe_pcm_dt_match, .pm = &mtk_afe_pm_ops, }, diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index 5ae5ca1..e093261 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -308,13 +308,7 @@ static struct snd_soc_platform_driver nuc900_soc_platform = { static int nuc900_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform); -} - -static int nuc900_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform); } static struct platform_driver nuc900_pcm_driver = { @@ -323,7 +317,6 @@ static struct platform_driver nuc900_pcm_driver = { }, .probe = nuc900_soc_platform_probe, - .remove = nuc900_soc_platform_remove, }; module_platform_driver(nuc900_pcm_driver); diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 68a1252..c7563e2 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -965,25 +965,15 @@ int omap_mcbsp_init(struct platform_device *pdev) mcbsp->free = true; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) { + if (!res) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(mcbsp->dev, "invalid memory resource\n"); - return -ENOMEM; - } - } - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - dev_name(&pdev->dev))) { - dev_err(mcbsp->dev, "memory region already claimed\n"); - return -ENODEV; - } + + mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcbsp->io_base)) + return PTR_ERR(mcbsp->io_base); mcbsp->phys_base = res->start; mcbsp->reg_cache_size = resource_size(res); - mcbsp->io_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!mcbsp->io_base) - return -ENOMEM; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); if (!res) diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index aeef25c..584b237 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -81,7 +81,15 @@ static int hdmi_dai_startup(struct snd_pcm_substream *substream, ret = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); if (ret < 0) { - dev_err(dai->dev, "could not apply constraint\n"); + dev_err(dai->dev, "Could not apply period constraint: %d\n", + ret); + return ret; + } + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128); + if (ret < 0) { + dev_err(dai->dev, "Could not apply buffer constraint: %d\n", + ret); return ret; } diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index 076bec6..732e749 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -154,8 +154,7 @@ static const struct snd_soc_dapm_route omap3pandora_map[] = { static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; /* All TWL4030 output pins are floating */ snd_soc_dapm_nc_pin(dapm, "EARPIECE"); @@ -174,8 +173,7 @@ static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; /* Not comnnected */ snd_soc_dapm_nc_pin(dapm, "HSMIC"); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 1eb45dc..51e790d 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -232,13 +232,7 @@ static int mmp_pcm_probe(struct platform_device *pdev) mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max = pdata->period_max_capture; } - return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform); -} - -static int mmp_pcm_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, &mmp_soc_platform); } static struct platform_driver mmp_pcm_driver = { @@ -247,7 +241,6 @@ static struct platform_driver mmp_pcm_driver = { }, .probe = mmp_pcm_probe, - .remove = mmp_pcm_remove, }; module_platform_driver(mmp_pcm_driver); diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index fbe2e93..3da485e 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -813,14 +813,8 @@ static const struct of_device_id pxa_ssp_of_ids[] = { static int asoc_ssp_probe(struct platform_device *pdev) { - return snd_soc_register_component(&pdev->dev, &pxa_ssp_component, - &pxa_ssp_dai, 1); -} - -static int asoc_ssp_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - return 0; + return devm_snd_soc_register_component(&pdev->dev, &pxa_ssp_component, + &pxa_ssp_dai, 1); } static struct platform_driver asoc_ssp_driver = { @@ -830,7 +824,6 @@ static struct platform_driver asoc_ssp_driver = { }, .probe = asoc_ssp_probe, - .remove = asoc_ssp_remove, }; module_platform_driver(asoc_ssp_driver); diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index e68290c..6b4e400 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -367,19 +367,12 @@ static const struct snd_soc_component_driver pxa_i2s_component = { static int pxa2xx_i2s_drv_probe(struct platform_device *pdev) { - return snd_soc_register_component(&pdev->dev, &pxa_i2s_component, - &pxa_i2s_dai, 1); -} - -static int pxa2xx_i2s_drv_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - return 0; + return devm_snd_soc_register_component(&pdev->dev, &pxa_i2s_component, + &pxa_i2s_dai, 1); } static struct platform_driver pxa2xx_i2s_driver = { .probe = pxa2xx_i2s_drv_probe, - .remove = pxa2xx_i2s_drv_remove, .driver = { .name = "pxa2xx-i2s", diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index a51c9da..831ee37 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -124,13 +124,7 @@ static struct snd_soc_platform_driver pxa2xx_soc_platform = { static int pxa2xx_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform); -} - -static int pxa2xx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform); } #ifdef CONFIG_OF @@ -147,7 +141,6 @@ static struct platform_driver pxa_pcm_driver = { }, .probe = pxa2xx_soc_platform_probe, - .remove = pxa2xx_soc_platform_remove, }; module_platform_driver(pxa_pcm_driver); diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 807fedf..3cc252e 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -1,5 +1,6 @@ config SND_SOC_QCOM tristate "ASoC support for QCOM platforms" + depends on ARCH_QCOM || COMPILE_TEST help Say Y or M if you want to add support to use audio devices in Qualcomm Technologies SOC-based platforms. @@ -14,19 +15,17 @@ config SND_SOC_LPASS_PLATFORM config SND_SOC_LPASS_IPQ806X tristate - depends on SND_SOC_QCOM select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM config SND_SOC_LPASS_APQ8016 tristate - depends on SND_SOC_QCOM select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" - depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST) + depends on SND_SOC_QCOM select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -35,7 +34,7 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST) + depends on SND_SOC_QCOM select SND_SOC_LPASS_APQ8016 help Support for Qualcomm Technologies LPASS audio block in diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 23f3d59..97bc202 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -235,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, return ret; } -struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { +const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { .set_sysclk = lpass_cpu_daiops_set_sysclk, .startup = lpass_cpu_daiops_startup, .shutdown = lpass_cpu_daiops_shutdown, diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 7356d3a..7a41679 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -73,7 +73,7 @@ static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) return 0; } -struct lpass_variant ipq806x_data = { +static struct lpass_variant ipq806x_data = { .i2sctrl_reg_base = 0x0010, .i2sctrl_reg_stride = 0x04, .i2s_ports = 5, diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index d6e86c1..0b63e2e 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -93,6 +93,6 @@ int asoc_qcom_lpass_platform_register(struct platform_device *); int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev); int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev); int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai); -extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops; +extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops; #endif /* __LPASS_H__ */ diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index e181826..58bae8e 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -14,3 +14,22 @@ config SND_SOC_ROCKCHIP_I2S Say Y or M if you want to add support for I2S driver for Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. + +config SND_SOC_ROCKCHIP_MAX98090 + tristate "ASoC support for Rockchip boards using a MAX98090 codec" + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB + select SND_SOC_ROCKCHIP_I2S + select SND_SOC_MAX98090 + select SND_SOC_TS3A227E + help + Say Y or M here if you want to add support for SoC audio on Rockchip + boards using the MAX98090 codec, such as Veyron. + +config SND_SOC_ROCKCHIP_RT5645 + tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB + select SND_SOC_ROCKCHIP_I2S + select SND_SOC_RT5645 + help + Say Y or M here if you want to add support for SoC audio on Rockchip + boards using the RT5645/RT5650 codec, such as Veyron. diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index b921909..1bc1dc3 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -2,3 +2,9 @@ snd-soc-i2s-objs := rockchip_i2s.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o + +snd-soc-rockchip-max98090-objs := rockchip_max98090.o +snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o + +obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index acb5be5..b936102 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -483,16 +483,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_suspend; } - ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); - goto err_pcm_register; + return ret; } return 0; -err_pcm_register: - snd_dmaengine_pcm_unregister(&pdev->dev); err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) i2s_runtime_suspend(&pdev->dev); @@ -512,8 +510,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev) clk_disable_unprepare(i2s->mclk); clk_disable_unprepare(i2s->hclk); - snd_dmaengine_pcm_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); return 0; } diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c new file mode 100644 index 0000000..26567b1 --- /dev/null +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -0,0 +1,236 @@ +/* + * Rockchip machine ASoC driver for boards using a MAX90809 CODEC. + * + * Copyright (c) 2014, ROCKCHIP CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "rockchip_i2s.h" +#include "../codecs/ts3a227e.h" + +#define DRV_NAME "rockchip-snd-max98090" + +static struct snd_soc_jack headset_jack; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headset Jack", + .mask = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + }, +}; + +static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route rk_audio_map[] = { + {"IN34", NULL, "Headset Mic"}, + {"IN34", NULL, "MICBIAS"}, + {"MICBIAS", NULL, "Headset Mic"}, + {"DMICL", NULL, "Int Mic"}, + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"Speaker", NULL, "SPKL"}, + {"Speaker", NULL, "SPKR"}, +}; + +static const struct snd_kcontrol_new rk_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int rk_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int mclk; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + mclk = 12288000; + break; + case 44100: + mclk = 11289600; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + return ret; + } + + return ret; +} + +static int rk_init(struct snd_soc_pcm_runtime *runtime) +{ + /* Enable Headset and 4 Buttons Jack detection */ + return snd_soc_card_jack_new(runtime->card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); +} + +static int rk_98090_headset_init(struct snd_soc_component *component) +{ + return ts3a227e_enable_jack_detect(component, &headset_jack); +} + +static struct snd_soc_ops rk_aif1_ops = { + .hw_params = rk_aif1_hw_params, +}; + +static struct snd_soc_aux_dev rk_98090_headset_dev = { + .name = "Headset Chip", + .init = rk_98090_headset_init, +}; + +static struct snd_soc_dai_link rk_dailink = { + .name = "max98090", + .stream_name = "Audio", + .codec_dai_name = "HiFi", + .init = rk_init, + .ops = &rk_aif1_ops, + /* set max98090 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_card_rk = { + .name = "ROCKCHIP-I2S", + .owner = THIS_MODULE, + .dai_link = &rk_dailink, + .num_links = 1, + .aux_dev = &rk_98090_headset_dev, + .num_aux_devs = 1, + .dapm_widgets = rk_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets), + .dapm_routes = rk_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_audio_map), + .controls = rk_mc_controls, + .num_controls = ARRAY_SIZE(rk_mc_controls), +}; + +static int snd_rk_mc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct snd_soc_card *card = &snd_soc_card_rk; + struct device_node *np = pdev->dev.of_node; + + /* register the soc card */ + card->dev = &pdev->dev; + + rk_dailink.codec_of_node = of_parse_phandle(np, + "rockchip,audio-codec", 0); + if (!rk_dailink.codec_of_node) { + dev_err(&pdev->dev, + "Property 'rockchip,audio-codec' missing or invalid\n"); + return -EINVAL; + } + + rk_dailink.cpu_of_node = of_parse_phandle(np, + "rockchip,i2s-controller", 0); + if (!rk_dailink.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'rockchip,i2s-controller' missing or invalid\n"); + return -EINVAL; + } + + rk_dailink.platform_of_node = rk_dailink.cpu_of_node; + + rk_98090_headset_dev.codec_of_node = of_parse_phandle(np, + "rockchip,headset-codec", 0); + if (!rk_98090_headset_dev.codec_of_node) { + dev_err(&pdev->dev, + "Property 'rockchip,headset-codec' missing/invalid\n"); + return -EINVAL; + } + + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); + if (ret) { + dev_err(&pdev->dev, + "Soc parse card name failed %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, + "Soc register card failed %d\n", ret); + return ret; + } + + return ret; +} + +static const struct of_device_id rockchip_max98090_of_match[] = { + { .compatible = "rockchip,rockchip-audio-max98090", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rockchip_max98090_of_match); + +static struct platform_driver snd_rk_mc_driver = { + .probe = snd_rk_mc_probe, + .driver = { + .name = DRV_NAME, + .pm = &snd_soc_pm_ops, + .of_match_table = rockchip_max98090_of_match, + }, +}; + +module_platform_driver(snd_rk_mc_driver); + +MODULE_AUTHOR("jianqun <jay.xu@rock-chips.com>"); +MODULE_DESCRIPTION("Rockchip max98090 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c new file mode 100644 index 0000000..68c62e4 --- /dev/null +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -0,0 +1,225 @@ +/* + * Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC. + * + * Copyright (c) 2015, ROCKCHIP CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "rockchip_i2s.h" + +#define DRV_NAME "rockchip-snd-rt5645" + +static struct snd_soc_jack headset_jack; + +/* Jack detect via rt5645 driver. */ +extern int rt5645_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, + struct snd_soc_jack *btn_jack); + +static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static const struct snd_soc_dapm_route rk_audio_map[] = { + /* Input Lines */ + {"DMIC L2", NULL, "Int Mic"}, + {"DMIC R2", NULL, "Int Mic"}, + {"RECMIXL", NULL, "Headset Mic"}, + {"RECMIXR", NULL, "Headset Mic"}, + + /* Output Lines */ + {"Headphones", NULL, "HPOR"}, + {"Headphones", NULL, "HPOL"}, + {"Speakers", NULL, "SPOL"}, + {"Speakers", NULL, "SPOR"}, +}; + +static const struct snd_kcontrol_new rk_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static int rk_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int mclk; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + mclk = 12288000; + break; + case 44100: + mclk = 11289600; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + return ret; + } + + return ret; +} + +static int rk_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + int ret; + + /* Enable Headset and 4 Buttons Jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &headset_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret); + return ret; + } + + return rt5645_set_jack_detect(runtime->codec, + &headset_jack, + &headset_jack, + &headset_jack); +} + +static struct snd_soc_ops rk_aif1_ops = { + .hw_params = rk_aif1_hw_params, +}; + +static struct snd_soc_dai_link rk_dailink = { + .name = "rt5645", + .stream_name = "rt5645 PCM", + .codec_dai_name = "rt5645-aif1", + .init = rk_init, + .ops = &rk_aif1_ops, + /* set rt5645 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_card_rk = { + .name = "I2S-RT5650", + .owner = THIS_MODULE, + .dai_link = &rk_dailink, + .num_links = 1, + .dapm_widgets = rk_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets), + .dapm_routes = rk_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_audio_map), + .controls = rk_mc_controls, + .num_controls = ARRAY_SIZE(rk_mc_controls), +}; + +static int snd_rk_mc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct snd_soc_card *card = &snd_soc_card_rk; + struct device_node *np = pdev->dev.of_node; + + /* register the soc card */ + card->dev = &pdev->dev; + + rk_dailink.codec_of_node = of_parse_phandle(np, + "rockchip,audio-codec", 0); + if (!rk_dailink.codec_of_node) { + dev_err(&pdev->dev, + "Property 'rockchip,audio-codec' missing or invalid\n"); + return -EINVAL; + } + + rk_dailink.cpu_of_node = of_parse_phandle(np, + "rockchip,i2s-controller", 0); + if (!rk_dailink.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'rockchip,i2s-controller' missing or invalid\n"); + return -EINVAL; + } + + rk_dailink.platform_of_node = rk_dailink.cpu_of_node; + + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); + if (ret) { + dev_err(&pdev->dev, + "Soc parse card name failed %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, + "Soc register card failed %d\n", ret); + return ret; + } + + return ret; +} + +static const struct of_device_id rockchip_rt5645_of_match[] = { + { .compatible = "rockchip,rockchip-audio-rt5645", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match); + +static struct platform_driver snd_rk_mc_driver = { + .probe = snd_rk_mc_probe, + .driver = { + .name = DRV_NAME, + .pm = &snd_soc_pm_ops, + .of_match_table = rockchip_rt5645_of_match, + }, +}; + +module_platform_driver(snd_rk_mc_driver); + +MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>"); +MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c index 8bf2e2c..ee1fda9 100644 --- a/sound/soc/samsung/arndale_rt5631.c +++ b/sound/soc/samsung/arndale_rt5631.c @@ -71,6 +71,7 @@ static struct snd_soc_dai_link arndale_rt5631_dai[] = { static struct snd_soc_card arndale_rt5631 = { .name = "Arndale RT5631", + .owner = THIS_MODULE, .dai_link = arndale_rt5631_dai, .num_links = ARRAY_SIZE(arndale_rt5631_dai), }; @@ -116,15 +117,6 @@ static int arndale_audio_probe(struct platform_device *pdev) return ret; } -static int arndale_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = { { .compatible = "samsung,arndale-rt5631", }, { .compatible = "samsung,arndale-alc5631", }, @@ -139,7 +131,6 @@ static struct platform_driver arndale_audio_driver = { .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), }, .probe = arndale_audio_probe, - .remove = arndale_audio_remove, }; module_platform_driver(arndale_audio_driver); diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 7651dc9..07ce2cf 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -56,6 +56,7 @@ static int snow_late_probe(struct snd_soc_card *card) static struct snd_soc_card snow_snd = { .name = "Snow-I2S", + .owner = THIS_MODULE, .dai_link = snow_dai, .num_links = ARRAY_SIZE(snow_dai), diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index fd11404..8fad444 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -327,13 +327,7 @@ static struct snd_soc_platform_driver sh7760_soc_platform = { static int sh7760_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &sh7760_soc_platform); -} - -static int sh7760_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, &sh7760_soc_platform); } static struct platform_driver sh7760_pcm_driver = { @@ -342,7 +336,6 @@ static struct platform_driver sh7760_pcm_driver = { }, .probe = sh7760_soc_platform_probe, - .remove = sh7760_soc_platform_remove, }; module_platform_driver(sh7760_pcm_driver); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 142c066..0215c78 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1911,7 +1911,6 @@ MODULE_DEVICE_TABLE(of, fsi_of_match); static const struct platform_device_id fsi_id_table[] = { { "sh_fsi", (kernel_ulong_t)&fsi1_core }, - { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, {}, }; MODULE_DEVICE_TABLE(platform, fsi_id_table); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index f1b4451..8b25850 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,4 +1,4 @@ -snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o +snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o snd-soc-rsrc-card-objs := rsrc-card.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f1e5920..f3feed5 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -203,9 +203,9 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) } /* - * settting function + * ADINR function */ -u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); @@ -227,6 +227,64 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) return adinr; } +u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct device *dev = rsnd_priv_to_dev(priv); + u32 chan = runtime->channels; + + switch (chan) { + case 1: + case 2: + case 4: + case 6: + case 8: + break; + default: + dev_warn(dev, "not supported channel\n"); + chan = 0; + break; + } + + return chan; +} + +/* + * DALIGN function + */ +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *target = src ? src : ssi; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 val = 0x76543210; + u32 mask = ~0; + + mask <<= runtime->channels * 4; + val = val & mask; + + switch (runtime->sample_bits) { + case 16: + val |= 0x67452301 & ~mask; + break; + case 32: + val |= 0x76543210 & ~mask; + break; + } + + /* + * exchange channeles on SRC if possible, + * otherwise, R/L volume settings on DVC + * changes inverted channels + */ + if (mod == target) + return val; + else + return 0x76543210; +} + /* * rsnd_dai functions */ @@ -242,9 +300,9 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) if (val == __rsnd_mod_call_##func) { \ called = 1; \ ret = (mod)->ops->func(mod, io, param); \ - mod->status = (mod->status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ } \ + mod->status = (mod->status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ called ? #func : ""); \ @@ -274,21 +332,21 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) static int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { + struct rsnd_priv *priv; + struct device *dev; + if (!mod) return -EIO; - if (io->mod[mod->type]) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_err(dev, "%s[%d] is not empty\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod)); - return -EIO; - } + priv = rsnd_mod_to_priv(mod); + dev = rsnd_priv_to_dev(priv); io->mod[mod->type] = mod; + dev_dbg(dev, "%s[%d] is connected to io (%s)\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_io_is_play(io) ? "Playback" : "Capture"); + return 0; } @@ -517,7 +575,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .set_fmt = rsnd_soc_dai_set_fmt, }; -#define rsnd_path_parse(priv, io, type) \ +#define rsnd_path_add(priv, io, type) \ ({ \ struct rsnd_mod *mod; \ int ret = 0; \ @@ -533,7 +591,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { ret; \ }) -#define rsnd_path_break(priv, io, type) \ +#define rsnd_path_remove(priv, io, type) \ { \ struct rsnd_mod *mod; \ int id = -1; \ @@ -547,6 +605,79 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { } \ } +void rsnd_path_parse(struct rsnd_priv *priv, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *cmd; + struct device *dev = rsnd_priv_to_dev(priv); + u32 data; + + /* Gen1 is not supported */ + if (rsnd_is_gen1(priv)) + return; + + if (!mix && !dvc) + return; + + if (mix) { + struct rsnd_dai *rdai; + int i; + u32 path[] = { + [0] = 0, + [1] = 1 << 0, + [2] = 0, + [3] = 0, + [4] = 0, + [5] = 1 << 8 + }; + + /* + * it is assuming that integrater is well understanding about + * data path. Here doesn't check impossible connection, + * like src2 + src5 + */ + data = 0; + for_each_rsnd_dai(rdai, priv, i) { + io = &rdai->playback; + if (mix == rsnd_io_to_mod_mix(io)) + data |= path[rsnd_mod_id(src)]; + + io = &rdai->capture; + if (mix == rsnd_io_to_mod_mix(io)) + data |= path[rsnd_mod_id(src)]; + } + + /* + * We can't use ctu = rsnd_io_ctu() here. + * Since, ID of dvc/mix are 0 or 1 (= same as CMD number) + * but ctu IDs are 0 - 7 (= CTU00 - CTU13) + */ + cmd = mix; + } else { + u32 path[] = { + [0] = 0x30000, + [1] = 0x30001, + [2] = 0x40000, + [3] = 0x10000, + [4] = 0x20000, + [5] = 0x40100 + }; + + data = path[rsnd_mod_id(src)]; + + cmd = dvc; + } + + dev_dbg(dev, "ctu/mix path = 0x%08x", data); + + rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data); + + rsnd_mod_write(cmd, CMD_CTRL, 0x10); +} + static int rsnd_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -564,18 +695,28 @@ static int rsnd_path_init(struct rsnd_priv *priv, * using fixed path. */ + /* SSI */ + ret = rsnd_path_add(priv, io, ssi); + if (ret < 0) + return ret; + /* SRC */ - ret = rsnd_path_parse(priv, io, src); + ret = rsnd_path_add(priv, io, src); if (ret < 0) return ret; - /* SSI */ - ret = rsnd_path_parse(priv, io, ssi); + /* CTU */ + ret = rsnd_path_add(priv, io, ctu); + if (ret < 0) + return ret; + + /* MIX */ + ret = rsnd_path_add(priv, io, mix); if (ret < 0) return ret; /* DVC */ - ret = rsnd_path_parse(priv, io, dvc); + ret = rsnd_path_add(priv, io, dvc); if (ret < 0) return ret; @@ -589,13 +730,15 @@ static void rsnd_of_parse_dai(struct platform_device *pdev, struct device_node *dai_node, *dai_np; struct device_node *ssi_node, *ssi_np; struct device_node *src_node, *src_np; + struct device_node *ctu_node, *ctu_np; + struct device_node *mix_node, *mix_np; struct device_node *dvc_node, *dvc_np; struct device_node *playback, *capture; struct rsnd_dai_platform_info *dai_info; struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct device *dev = &pdev->dev; int nr, i; - int dai_i, ssi_i, src_i, dvc_i; + int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i; if (!of_data) return; @@ -621,6 +764,8 @@ static void rsnd_of_parse_dai(struct platform_device *pdev, ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); + ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); + mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix"); dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc"); #define mod_parse(name) \ @@ -657,6 +802,8 @@ if (name##_node) { \ mod_parse(ssi); mod_parse(src); + mod_parse(ctu); + mod_parse(mix); mod_parse(dvc); of_node_put(playback); @@ -1033,8 +1180,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, /* * remove SRC/DVC from DAI, */ - rsnd_path_break(priv, io, src); - rsnd_path_break(priv, io, dvc); + rsnd_path_remove(priv, io, src); + rsnd_path_remove(priv, io, dvc); /* * fallback @@ -1069,6 +1216,8 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dma_probe, rsnd_ssi_probe, rsnd_src_probe, + rsnd_ctu_probe, + rsnd_mix_probe, rsnd_dvc_probe, rsnd_adg_probe, rsnd_dai_probe, @@ -1164,6 +1313,8 @@ static int rsnd_remove(struct platform_device *pdev) struct rsnd_priv *priv) = { rsnd_ssi_remove, rsnd_src_remove, + rsnd_ctu_remove, + rsnd_mix_remove, rsnd_dvc_remove, }; int ret = 0, i; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c new file mode 100644 index 0000000..05498bb --- /dev/null +++ b/sound/soc/sh/rcar/ctu.c @@ -0,0 +1,171 @@ +/* + * ctu.c + * + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +#define CTU_NAME_SIZE 16 +#define CTU_NAME "ctu" + +struct rsnd_ctu { + struct rsnd_ctu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_ctu_nr(priv) ((priv)->ctu_nr) +#define for_each_rsnd_ctu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_ctu_nr(priv)) && \ + ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ + i++) + +#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) +#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) +static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, CTU_CTUIR, enable); +} + +static int rsnd_ctu_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_hw_start(mod); + + rsnd_ctu_initialize_lock(mod); + + rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + + rsnd_ctu_initialize_unlock(mod); + + return 0; +} + +static int rsnd_ctu_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_hw_stop(mod); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ctu_ops = { + .name = CTU_NAME, + .init = rsnd_ctu_init, + .quit = rsnd_ctu_quit, +}; + +struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) + id = 0; + + return &((struct rsnd_ctu *)(priv->ctu) + id)->mod; +} + +static void rsnd_of_parse_ctu(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct device_node *node; + struct rsnd_ctu_platform_info *ctu_info; + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = &pdev->dev; + int nr; + + if (!of_data) + return; + + node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); + if (!node) + return; + + nr = of_get_child_count(node); + if (!nr) + goto rsnd_of_parse_ctu_end; + + ctu_info = devm_kzalloc(dev, + sizeof(struct rsnd_ctu_platform_info) * nr, + GFP_KERNEL); + if (!ctu_info) { + dev_err(dev, "ctu info allocation error\n"); + goto rsnd_of_parse_ctu_end; + } + + info->ctu_info = ctu_info; + info->ctu_info_nr = nr; + +rsnd_of_parse_ctu_end: + of_node_put(node); + +} + +int rsnd_ctu_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ctu *ctu; + struct clk *clk; + char name[CTU_NAME_SIZE]; + int i, nr, ret; + + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) { + dev_warn(dev, "CTU is not supported on Gen1\n"); + return -EINVAL; + } + + rsnd_of_parse_ctu(pdev, of_data, priv); + + nr = info->ctu_info_nr; + if (!nr) + return 0; + + ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); + if (!ctu) + return -ENOMEM; + + priv->ctu_nr = nr; + priv->ctu = ctu; + + for_each_rsnd_ctu(ctu, priv, i) { + /* + * CTU00, CTU01, CTU02, CTU03 => CTU0 + * CTU10, CTU11, CTU12, CTU13 => CTU1 + */ + snprintf(name, CTU_NAME_SIZE, "%s.%d", + CTU_NAME, i / 4); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ctu->info = &info->ctu_info[i]; + + ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops, + clk, RSND_MOD_CTU, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_ctu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) { + rsnd_mod_quit(&ctu->mod); + } +} diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index d306e29..bfbb8a5 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -27,6 +27,15 @@ struct rsnd_dma_ctrl { int dmapp_num; }; +struct rsnd_dma_ops { + char *name; + void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); + void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +}; + #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) /* @@ -168,7 +177,7 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, dma_cap_set(DMA_SLAVE, mask); dmaen->chan = dma_request_channel(mask, shdma_chan_filter, - (void *)id); + (void *)(uintptr_t)id); } if (IS_ERR_OR_NULL(dmaen->chan)) { dmaen->chan = NULL; @@ -182,7 +191,8 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dev_dbg(dev, "dma : %pad -> %pad\n", + dev_dbg(dev, "%s %pad -> %pad\n", + dma->ops->name, &cfg.src_addr, &cfg.dst_addr); ret = dmaengine_slave_config(dmaen->chan, &cfg); @@ -215,6 +225,7 @@ static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) } static struct rsnd_dma_ops rsnd_dmaen_ops = { + .name = "audmac", .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, .init = rsnd_dmaen_init, @@ -360,6 +371,7 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io, } static struct rsnd_dma_ops rsnd_dmapp_ops = { + .name = "audmac-pp", .start = rsnd_dmapp_start, .stop = rsnd_dmapp_stop, .init = rsnd_dmapp_init, @@ -414,7 +426,9 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); int use_src = !!rsnd_io_to_mod_src(io); - int use_dvc = !!rsnd_io_to_mod_dvc(io); + int use_cmd = !!rsnd_io_to_mod_dvc(io) || + !!rsnd_io_to_mod_mix(io) || + !!rsnd_io_to_mod_ctu(io); int id = rsnd_mod_id(mod); struct dma_addr { dma_addr_t out_addr; @@ -452,7 +466,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, }; /* it shouldn't happen */ - if (use_dvc && !use_src) + if (use_cmd && !use_src) dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ @@ -460,8 +474,8 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, is_ssi++; return (is_from) ? - dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : - dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; + dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr : + dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; } static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, @@ -482,7 +496,7 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } -#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ +#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ static void rsnd_dma_of_path(struct rsnd_dma *dma, struct rsnd_dai_stream *io, int is_play, @@ -492,55 +506,81 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, struct rsnd_mod *this = rsnd_dma_to_mod(dma); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_mod *mod[MOD_MAX]; - int i, index; + struct rsnd_mod *mod_start, *mod_end; + struct rsnd_priv *priv = rsnd_mod_to_priv(this); + struct device *dev = rsnd_priv_to_dev(priv); + int nr, i; + if (!ssi) + return; - for (i = 0; i < MOD_MAX; i++) + nr = 0; + for (i = 0; i < MOD_MAX; i++) { mod[i] = NULL; + nr += !!rsnd_io_to_mod(io, i); + } /* - * in play case... + * [S] -*-> [E] + * [S] -*-> SRC -o-> [E] + * [S] -*-> SRC -> DVC -o-> [E] + * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E] * - * src -> dst + * playback [S] = mem + * [E] = SSI * - * mem -> SSI - * mem -> SRC -> SSI - * mem -> SRC -> DVC -> SSI + * capture [S] = SSI + * [E] = mem + * + * -*-> Audio DMAC + * -o-> Audio DMAC peri peri */ - mod[0] = NULL; /* for "mem" */ - index = 1; - for (i = 1; i < MOD_MAX; i++) { - if (!src) { - mod[i] = ssi; - } else if (!dvc) { - mod[i] = src; - src = NULL; - } else { - if ((!is_play) && (this == src)) - this = dvc; + mod_start = (is_play) ? NULL : ssi; + mod_end = (is_play) ? ssi : NULL; - mod[i] = (is_play) ? src : dvc; - i++; - mod[i] = (is_play) ? dvc : src; + mod[0] = mod_start; + for (i = 1; i < nr; i++) { + if (src) { + mod[i] = src; src = NULL; + } else if (ctu) { + mod[i] = ctu; + ctu = NULL; + } else if (mix) { + mod[i] = mix; + mix = NULL; + } else if (dvc) { + mod[i] = dvc; dvc = NULL; } - - if (mod[i] == this) - index = i; - - if (mod[i] == ssi) - break; } + mod[i] = mod_end; - if (is_play) { - *mod_from = mod[index - 1]; - *mod_to = mod[index]; + /* + * | SSI | SRC | + * -------------+-----+-----+ + * is_play | o | * | + * !is_play | * | o | + */ + if ((this == ssi) == (is_play)) { + *mod_from = mod[nr - 1]; + *mod_to = mod[nr]; } else { - *mod_from = mod[index]; - *mod_to = mod[index - 1]; + *mod_from = mod[0]; + *mod_to = mod[1]; + } + + dev_dbg(dev, "module connection (this is %s[%d])\n", + rsnd_mod_name(this), rsnd_mod_id(this)); + for (i = 0; i <= nr; i++) { + dev_dbg(dev, " %s[%d]%s\n", + rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), + (mod[i] == *mod_from) ? " from" : + (mod[i] == *mod_to) ? " to" : ""); } } @@ -568,10 +608,11 @@ void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) { - struct rsnd_mod *mod_from; - struct rsnd_mod *mod_to; + struct rsnd_mod *mod_from = NULL; + struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); int is_play = rsnd_io_is_play(io); /* @@ -598,6 +639,11 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) if (rsnd_is_gen1(priv)) dma->ops = &rsnd_dmaen_ops; + dev_dbg(dev, "%s %s[%d] -> %s[%d]\n", + dma->ops->name, + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + return dma->ops->init(io, dma, id, mod_from, mod_to); } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 36fc020..5779638 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -24,6 +24,7 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; +#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) #define rsnd_dvc_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") @@ -63,6 +64,19 @@ static const char * const dvc_ramp_rate[] = { "0.125 dB/8192 steps", /* 10111 */ }; +static void rsnd_dvc_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, DVC_SWRSR, 0); + rsnd_mod_write(mod, DVC_SWRSR, 1); +} + +#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1) +#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0) +static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, DVC_DVUIR, enable); +} + static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -135,49 +149,24 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, return 0; } -static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, +static int rsnd_dvc_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct device *dev = rsnd_priv_to_dev(priv); - int dvc_id = rsnd_mod_id(dvc_mod); - int src_id = rsnd_mod_id(src_mod); - u32 route[] = { - [0] = 0x30000, - [1] = 0x30001, - [2] = 0x40000, - [3] = 0x10000, - [4] = 0x20000, - [5] = 0x40100 - }; - - if (src_id >= ARRAY_SIZE(route)) { - dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id); - return -EINVAL; - } - - rsnd_mod_hw_start(dvc_mod); + rsnd_mod_hw_start(mod); - /* - * fixme - * it doesn't support CTU/MIX - */ - rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]); + rsnd_dvc_soft_reset(mod); - rsnd_mod_write(dvc_mod, DVC_SWRSR, 0); - rsnd_mod_write(dvc_mod, DVC_SWRSR, 1); + rsnd_dvc_initialize_lock(mod); - rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); + rsnd_path_parse(priv, io); - rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io)); + rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io)); /* ch0/ch1 Volume */ - rsnd_dvc_volume_update(io, dvc_mod); + rsnd_dvc_volume_update(io, mod); - rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); - - rsnd_adg_set_cmd_timsel_gen2(dvc_mod, io); + rsnd_adg_set_cmd_timsel_gen2(mod, io); return 0; } @@ -195,6 +184,8 @@ static int rsnd_dvc_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_dvc_initialize_unlock(mod); + rsnd_mod_write(mod, CMD_CTRL, 0x10); return 0; @@ -341,23 +332,21 @@ int rsnd_dvc_probe(struct platform_device *pdev, char name[RSND_DVC_NAME_SIZE]; int i, nr, ret; - rsnd_of_parse_dvc(pdev, of_data, priv); - - nr = info->dvc_info_nr; - if (!nr) - return 0; - /* This driver doesn't support Gen1 at this point */ if (rsnd_is_gen1(priv)) { dev_warn(dev, "CMD is not supported on Gen1\n"); return -EINVAL; } + rsnd_of_parse_dvc(pdev, of_data, priv); + + nr = info->dvc_info_nr; + if (!nr) + return 0; + dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); - if (!dvc) { - dev_err(dev, "CMD allocate failed\n"); + if (!dvc) return -ENOMEM; - } priv->dvc_nr = nr; priv->dvc = dvc; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 8c7dc51..f04d17b 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -103,6 +103,22 @@ void rsnd_write(struct rsnd_priv *priv, regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); } +void rsnd_force_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + + dev_dbg(dev, "w %s[%d] - %4d : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); + + regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); +} + void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data) { @@ -200,12 +216,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80), - RSND_GEN_M_REG(BUSIF_DALIGN, 0x8, 0x80), + RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80), RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), - RSND_GEN_M_REG(INT_ENABLE, 0x18, 0x80), + RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), }; struct rsnd_regmap_field_conf conf_scu[] = { RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), + RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), @@ -223,6 +240,18 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), + RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), + RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), + RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), + RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), + RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40), + RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40), + RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40), + RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40), + RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40), + RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40), + RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40), RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100), RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100), RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100), diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c new file mode 100644 index 0000000..0d5c102 --- /dev/null +++ b/sound/soc/sh/rcar/mix.c @@ -0,0 +1,200 @@ +/* + * mix.c + * + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +#define MIX_NAME_SIZE 16 +#define MIX_NAME "mix" + +struct rsnd_mix { + struct rsnd_mix_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_mix_nr(priv) ((priv)->mix_nr) +#define for_each_rsnd_mix(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_mix_nr(priv)) && \ + ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ + i++) + + +static void rsnd_mix_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, MIX_SWRSR, 0); + rsnd_mod_write(mod, MIX_SWRSR, 1); +} + +#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1) +#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0) +static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, MIX_MIXIR, enable); +} + +static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + + /* Disable MIX dB setting */ + rsnd_mod_write(mod, MIX_MDBER, 0); + + rsnd_mod_write(mod, MIX_MDBAR, 0); + rsnd_mod_write(mod, MIX_MDBBR, 0); + rsnd_mod_write(mod, MIX_MDBCR, 0); + rsnd_mod_write(mod, MIX_MDBDR, 0); + + /* Enable MIX dB setting */ + rsnd_mod_write(mod, MIX_MDBER, 1); +} + +static int rsnd_mix_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_hw_start(mod); + + rsnd_mix_soft_reset(mod); + + rsnd_mix_initialize_lock(mod); + + rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); + + rsnd_path_parse(priv, io); + + /* volume step */ + rsnd_mod_write(mod, MIX_MIXMR, 0); + rsnd_mod_write(mod, MIX_MVPDR, 0); + + rsnd_mix_volume_update(io, mod); + + rsnd_mix_initialize_unlock(mod); + + return 0; +} + +static int rsnd_mix_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_hw_stop(mod); + + return 0; +} + +static struct rsnd_mod_ops rsnd_mix_ops = { + .name = MIX_NAME, + .init = rsnd_mix_init, + .quit = rsnd_mix_quit, +}; + +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) + id = 0; + + return &((struct rsnd_mix *)(priv->mix) + id)->mod; +} + +static void rsnd_of_parse_mix(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct device_node *node; + struct rsnd_mix_platform_info *mix_info; + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = &pdev->dev; + int nr; + + if (!of_data) + return; + + node = of_get_child_by_name(dev->of_node, "rcar_sound,mix"); + if (!node) + return; + + nr = of_get_child_count(node); + if (!nr) + goto rsnd_of_parse_mix_end; + + mix_info = devm_kzalloc(dev, + sizeof(struct rsnd_mix_platform_info) * nr, + GFP_KERNEL); + if (!mix_info) { + dev_err(dev, "mix info allocation error\n"); + goto rsnd_of_parse_mix_end; + } + + info->mix_info = mix_info; + info->mix_info_nr = nr; + +rsnd_of_parse_mix_end: + of_node_put(node); + +} + +int rsnd_mix_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix; + struct clk *clk; + char name[MIX_NAME_SIZE]; + int i, nr, ret; + + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) { + dev_warn(dev, "MIX is not supported on Gen1\n"); + return -EINVAL; + } + + rsnd_of_parse_mix(pdev, of_data, priv); + + nr = info->mix_info_nr; + if (!nr) + return 0; + + mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); + if (!mix) + return -ENOMEM; + + priv->mix_nr = nr; + priv->mix = mix; + + for_each_rsnd_mix(mix, priv, i) { + snprintf(name, MIX_NAME_SIZE, "%s.%d", + MIX_NAME, i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + mix->info = &info->mix_info[i]; + + ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops, + clk, RSND_MOD_MIX, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_mix_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) { + rsnd_mod_quit(&mix->mod); + } +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 09fcc54..7a0e52b 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -47,6 +47,18 @@ enum rsnd_reg { RSND_REG_SCU_SYS_STATUS0, RSND_REG_SCU_SYS_INT_EN0, RSND_REG_CMD_ROUTE_SLCT, + RSND_REG_CTU_CTUIR, + RSND_REG_CTU_ADINR, + RSND_REG_MIX_SWRSR, + RSND_REG_MIX_MIXIR, + RSND_REG_MIX_ADINR, + RSND_REG_MIX_MIXMR, + RSND_REG_MIX_MVPDR, + RSND_REG_MIX_MDBAR, + RSND_REG_MIX_MDBBR, + RSND_REG_MIX_MDBCR, + RSND_REG_MIX_MDBDR, + RSND_REG_MIX_MDBER, RSND_REG_DVC_SWRSR, RSND_REG_DVC_DVUIR, RSND_REG_DVC_ADINR, @@ -99,6 +111,7 @@ enum rsnd_reg { RSND_REG_SHARE26, RSND_REG_SHARE27, RSND_REG_SHARE28, + RSND_REG_SHARE29, RSND_REG_MAX, }; @@ -119,7 +132,7 @@ enum rsnd_reg { #define RSND_REG_SSI_CTRL RSND_REG_SHARE02 #define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03 #define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04 -#define RSND_REG_INT_ENABLE RSND_REG_SHARE05 +#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05 #define RSND_REG_SRC_BSDSR RSND_REG_SHARE06 #define RSND_REG_SRC_BSISR RSND_REG_SHARE07 #define RSND_REG_DIV_EN RSND_REG_SHARE08 @@ -136,13 +149,14 @@ enum rsnd_reg { #define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19 #define RSND_REG_CMD_CTRL RSND_REG_SHARE20 #define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21 -#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22 +#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22 #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 #define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 #define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 #define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28 +#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29 struct rsnd_of_data; struct rsnd_priv; @@ -157,27 +171,28 @@ struct rsnd_dai_stream; rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) #define rsnd_mod_write(m, r, d) \ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_force_write(m, r, d) \ + rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); +void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); -u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +void rsnd_path_parse(struct rsnd_priv *priv, + struct rsnd_dai_stream *io); /* * R-Car DMA */ struct rsnd_dma; -struct rsnd_dma_ops { - void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); - void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); - int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); - void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); -}; struct rsnd_dmaen { struct dma_chan *chan; @@ -217,6 +232,8 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, */ enum rsnd_mod_type { RSND_MOD_DVC = 0, + RSND_MOD_MIX, + RSND_MOD_CTU, RSND_MOD_SRC, RSND_MOD_SSI, RSND_MOD_MAX, @@ -312,7 +329,7 @@ struct rsnd_mod { #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -#define rsnd_mod_id(mod) ((mod)->id) +#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) #define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) @@ -345,9 +362,12 @@ struct rsnd_dai_stream { int byte_per_period; int next_period_byte; }; -#define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI]) -#define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC]) -#define rsnd_io_to_mod_dvc(io) ((io)->mod[RSND_MOD_DVC]) +#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) +#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) +#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) +#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) +#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) +#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC) #define rsnd_io_to_rdai(io) ((io)->rdai) #define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io))) #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) @@ -437,12 +457,6 @@ struct rsnd_priv { void *gen; /* - * below value will be filled on rsnd_src_probe() - */ - void *src; - int src_nr; - - /* * below value will be filled on rsnd_adg_probe() */ void *adg; @@ -459,6 +473,24 @@ struct rsnd_priv { int ssi_nr; /* + * below value will be filled on rsnd_src_probe() + */ + void *src; + int src_nr; + + /* + * below value will be filled on rsnd_ctu_probe() + */ + void *ctu; + int ctu_nr; + + /* + * below value will be filled on rsnd_mix_probe() + */ + void *mix; + int mix_nr; + + /* * below value will be filled on rsnd_dvc_probe() */ void *dvc; @@ -531,6 +563,19 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, u32 max); /* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + +/* * R-Car SRC */ int rsnd_src_probe(struct platform_device *pdev, @@ -550,20 +595,27 @@ int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod); int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod); -#define rsnd_src_nr(priv) ((priv)->src_nr) +/* + * R-Car CTU + */ +int rsnd_ctu_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); + +void rsnd_ctu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); /* - * R-Car SSI + * R-Car MIX */ -int rsnd_ssi_probe(struct platform_device *pdev, +int rsnd_mix_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); -void rsnd_ssi_remove(struct platform_device *pdev, + +void rsnd_mix_remove(struct platform_device *pdev, struct rsnd_priv *priv); -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); /* * R-Car DVC @@ -575,7 +627,4 @@ void rsnd_dvc_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) - - #endif diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 84e9357..d61db9c 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -41,6 +41,7 @@ static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { static const struct of_device_id rsrc_card_of_match[] = { { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, + { .compatible = "renesas,rsrc-card", }, {}, }; MODULE_DEVICE_TABLE(of, rsrc_card_of_match); @@ -242,8 +243,15 @@ static int rsrc_card_parse_links(struct device_node *np, snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); /* additional name prefix */ - priv->codec_conf.of_node = dai_link->codec_of_node; - priv->codec_conf.name_prefix = of_data->prefix; + if (of_data) { + priv->codec_conf.of_node = dai_link->codec_of_node; + priv->codec_conf.name_prefix = of_data->prefix; + } else { + snd_soc_of_parse_audio_prefix(&priv->snd_card, + &priv->codec_conf, + dai_link->codec_of_node, + "audio-prefix"); + } /* set dai_name */ snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s", @@ -361,8 +369,14 @@ static int rsrc_card_parse_of(struct device_node *node, priv->snd_card.num_links = num; priv->snd_card.codec_conf = &priv->codec_conf; priv->snd_card.num_configs = 1; - priv->snd_card.of_dapm_routes = of_data->routes; - priv->snd_card.num_of_dapm_routes = of_data->num_routes; + + if (of_data) { + priv->snd_card.of_dapm_routes = of_data->routes; + priv->snd_card.num_of_dapm_routes = of_data->num_routes; + } else { + snd_soc_of_parse_audio_routing(&priv->snd_card, + "audio-routing"); + } /* Parse the card name from DT */ snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index c61c171..89a18e1 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -30,6 +30,7 @@ struct rsnd_src { #define RSND_SRC_NAME_SIZE 16 +#define rsnd_src_nr(priv) ((priv)->src_nr) #define rsnd_enable_sync_convert(src) ((src)->sen.val) #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") @@ -117,6 +118,20 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ +static void rsnd_src_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SRC_SWRSR, 0); + rsnd_mod_write(mod, SRC_SWRSR, 1); +} + + +#define rsnd_src_initialize_lock(mod) __rsnd_src_initialize_lock(mod, 1) +#define rsnd_src_initialize_unlock(mod) __rsnd_src_initialize_lock(mod, 0) +static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, SRC_SRCIR, enable); +} + static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -133,7 +148,6 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int ssi_id = rsnd_mod_id(ssi_mod); /* @@ -170,27 +184,14 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, * DMA settings for SSIU */ if (use_busif) { - u32 val = 0x76543210; - u32 mask = ~0; + u32 val = rsnd_get_dalign(ssi_mod, io); rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, - rsnd_get_adinr(ssi_mod, io)); + rsnd_get_adinr_bit(ssi_mod, io)); rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); - mask <<= runtime->channels * 4; - val = val & mask; - - switch (runtime->sample_bits) { - case 16: - val |= 0x67452301 & ~mask; - break; - case 32: - val |= 0x76543210 & ~mask; - break; - } - rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val); - + rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val); } return 0; @@ -215,10 +216,9 @@ int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod) return 0; /* enable SSI interrupt if Gen2 */ - if (rsnd_ssi_is_dma_mode(ssi_mod)) - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000); - else - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, + rsnd_ssi_is_dma_mode(ssi_mod) ? + 0x0e000000 : 0x0f000000); return 0; } @@ -231,7 +231,7 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000); + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); return 0; } @@ -294,12 +294,8 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, if (convert_rate) fsrate = 0x0400000 / convert_rate * runtime->rate; - /* set/clear soft reset */ - rsnd_mod_write(mod, SRC_SWRSR, 0); - rsnd_mod_write(mod, SRC_SWRSR, 1); - /* Set channel number and output bit length */ - rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod, io)); + rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io)); /* Enable the initial value of IFS */ if (fsrate) { @@ -358,17 +354,15 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_mod_hw_start(mod); + rsnd_src_soft_reset(mod); + + rsnd_src_initialize_lock(mod); + src->err = 0; /* reset sync convert_rate */ src->sync.val = 0; - /* - * Initialize the operation of the SRC internal circuits - * see rsnd_src_start() - */ - rsnd_mod_write(mod, SRC_SRCIR, 1); - return 0; } @@ -395,11 +389,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, static int rsnd_src_start(struct rsnd_mod *mod) { - /* - * Cancel the initialization and operate the SRC function - * see rsnd_src_init() - */ - rsnd_mod_write(mod, SRC_SRCIR, 0); + rsnd_src_initialize_unlock(mod); return 0; } @@ -617,6 +607,14 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) int_val = 0; } + /* + * WORKAROUND + * + * ignore over flow error when rsnd_enable_sync_convert() + */ + if (rsnd_enable_sync_convert(src)) + sys_int_val = sys_int_val & 0xffff; + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); @@ -632,11 +630,22 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) { - u32 val = OUF_SRC(rsnd_mod_id(mod)); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val0, val1; bool ret = false; - if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || - (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { + val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); + + /* + * WORKAROUND + * + * ignore over flow error when rsnd_enable_sync_convert() + */ + if (rsnd_enable_sync_convert(src)) + val0 = val0 & 0xffff; + + if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) { struct rsnd_src *src = rsnd_mod_to_src(mod); src->err++; @@ -652,7 +661,20 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) static int _rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val; + + val = rsnd_get_dalign(mod, io); + + rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val); + + /* + * WORKAROUND + * + * Enable SRC output if you want to use sync convert together with DVC + */ + val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ? + 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -922,13 +944,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, return 0; /* - * We can't use SRC sync convert - * if it has DVC - */ - if (rsnd_io_to_mod_dvc(io)) - return 0; - - /* * enable sync convert */ ret = rsnd_kctrl_new_s(mod, io, rtd, @@ -1047,10 +1062,8 @@ int rsnd_src_probe(struct platform_device *pdev, return 0; src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); - if (!src) { - dev_err(dev, "SRC allocate failed\n"); + if (!src) return -ENOMEM; - } priv->src_nr = nr; priv->src = src; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2fbe59f..d45b9a7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -770,10 +770,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, */ nr = info->ssi_info_nr; ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); - if (!ssi) { - dev_err(dev, "SSI allocate failed\n"); + if (!ssi) return -ENOMEM; - } priv->ssi = ssi; priv->ssi_nr = nr; diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c index ab13146..89ed1b1 100644 --- a/sound/soc/sh/ssi.c +++ b/sound/soc/sh/ssi.c @@ -385,14 +385,9 @@ static const struct snd_soc_component_driver sh4_ssi_component = { static int sh4_soc_dai_probe(struct platform_device *pdev) { - return snd_soc_register_component(&pdev->dev, &sh4_ssi_component, - sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai)); -} - -static int sh4_soc_dai_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - return 0; + return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component, + sh4_ssi_dai, + ARRAY_SIZE(sh4_ssi_dai)); } static struct platform_driver sh4_ssi_driver = { @@ -401,7 +396,6 @@ static struct platform_driver sh4_ssi_driver = { }, .probe = sh4_soc_dai_probe, - .remove = sh4_soc_dai_remove, }; module_platform_driver(sh4_ssi_driver); diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 08d7259..d40efc9 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -85,10 +85,19 @@ EXPORT_SYMBOL(snd_soc_alloc_ac97_codec); /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec + * @id: The expected device ID + * @id_mask: Mask that is applied to the device ID before comparing with @id * * Initialises AC97 codec resources for use by ad-hoc devices only. + * + * If @id is not 0 this function will reset the device, then read the ID from + * the device and check if it matches the expected ID. If it doesn't match an + * error will be returned and device will not be registered. + * + * Returns: A PTR_ERR() on failure or a valid snd_ac97 struct on success. */ -struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec) +struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec, + unsigned int id, unsigned int id_mask) { struct snd_ac97 *ac97; int ret; @@ -97,13 +106,24 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec) if (IS_ERR(ac97)) return ac97; - ret = device_add(&ac97->dev); - if (ret) { - put_device(&ac97->dev); - return ERR_PTR(ret); + if (id) { + ret = snd_ac97_reset(ac97, false, id, id_mask); + if (ret < 0) { + dev_err(codec->dev, "Failed to reset AC97 device: %d\n", + ret); + goto err_put_device; + } } + ret = device_add(&ac97->dev); + if (ret) + goto err_put_device; + return ac97; + +err_put_device: + put_device(&ac97->dev); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0e1e69c..6173d15 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -654,10 +654,12 @@ int snd_soc_suspend(struct device *dev) /* suspend all CODECs */ list_for_each_entry(codec, &card->codec_dev_list, card_list) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + /* If there are paths active then the CODEC will be held with * bias _ON and should not be suspended. */ if (!codec->suspended) { - switch (codec->dapm.bias_level) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_STANDBY: /* * If the CODEC is capable of idle @@ -665,7 +667,7 @@ int snd_soc_suspend(struct device *dev) * means it's doing something, * otherwise fall through. */ - if (codec->dapm.idle_bias_off) { + if (dapm->idle_bias_off) { dev_dbg(codec->dev, "ASoC: idle_bias_off CODEC on over suspend\n"); break; @@ -978,7 +980,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) static void soc_remove_component(struct snd_soc_component *component) { - if (!component->probed) + if (!component->card) return; /* This is a HACK and will be removed soon */ @@ -991,7 +993,7 @@ static void soc_remove_component(struct snd_soc_component *component) snd_soc_dapm_free(snd_soc_component_get_dapm(component)); soc_cleanup_component_debugfs(component); - component->probed = 0; + component->card = NULL; module_put(component->dev->driver->owner); } @@ -1102,16 +1104,26 @@ static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_dai *dai; int ret; - if (component->probed) + if (!strcmp(component->name, "snd-soc-dummy")) return 0; - component->card = card; - dapm->card = card; - soc_set_name_prefix(card, component); + if (component->card) { + if (component->card != card) { + dev_err(component->dev, + "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", + card->name, component->card->name); + return -ENODEV; + } + return 0; + } if (!try_module_get(component->dev->driver->owner)) return -ENODEV; + component->card = card; + dapm->card = card; + soc_set_name_prefix(card, component); + soc_init_component_debugfs(component); if (component->dapm_widgets) { @@ -1155,7 +1167,6 @@ static int soc_probe_component(struct snd_soc_card *card, snd_soc_dapm_add_routes(dapm, component->dapm_routes, component->num_dapm_routes); - component->probed = 1; list_add(&dapm->list, &card->dapm_list); /* This is a HACK and will be removed soon */ @@ -1166,6 +1177,7 @@ static int soc_probe_component(struct snd_soc_card *card, err_probe: soc_cleanup_component_debugfs(component); + component->card = NULL; module_put(component->dev->driver->owner); return ret; @@ -1449,7 +1461,7 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) rtd->dev_registered = 0; } - if (component && component->probed) + if (component) soc_remove_component(component); } @@ -2128,7 +2140,7 @@ EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); /** * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. * @dai: DAI - * @ratio Ratio of BCLK to Sample rate. + * @ratio: Ratio of BCLK to Sample rate. * * Configures the DAI for a preset BCLK to sample rate ratio. */ @@ -2652,10 +2664,7 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->probe = component->driver->probe; component->remove = component->driver->remove; - if (!component->dapm_ptr) - component->dapm_ptr = &component->dapm; - - dapm = component->dapm_ptr; + dapm = &component->dapm; dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; @@ -2799,6 +2808,7 @@ EXPORT_SYMBOL_GPL(snd_soc_register_component); /** * snd_soc_unregister_component - Unregister a component from the ASoC core * + * @dev: The device to unregister */ void snd_soc_unregister_component(struct device *dev) { @@ -2839,7 +2849,7 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component) * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform * @platform: The platform to add - * @platform_driver: The driver for the platform + * @platform_drv: The driver for the platform */ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, const struct snd_soc_platform_driver *platform_drv) @@ -2878,7 +2888,8 @@ EXPORT_SYMBOL_GPL(snd_soc_add_platform); /** * snd_soc_register_platform - Register a platform with the ASoC core * - * @platform: platform to register + * @dev: The device for the platform + * @platform_drv: The driver for the platform */ int snd_soc_register_platform(struct device *dev, const struct snd_soc_platform_driver *platform_drv) @@ -2939,7 +2950,7 @@ EXPORT_SYMBOL_GPL(snd_soc_lookup_platform); /** * snd_soc_unregister_platform - Unregister a platform from the ASoC core * - * @platform: platform to unregister + * @dev: platform to unregister */ void snd_soc_unregister_platform(struct device *dev) { @@ -3030,13 +3041,17 @@ static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm, /** * snd_soc_register_codec - Register a codec with the ASoC core * - * @codec: codec to register + * @dev: The parent device for this codec + * @codec_drv: Codec driver + * @dai_drv: The associated DAI driver + * @num_dai: Number of DAIs */ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai) { + struct snd_soc_dapm_context *dapm; struct snd_soc_codec *codec; struct snd_soc_dai *dai; int ret, i; @@ -3047,7 +3062,6 @@ int snd_soc_register_codec(struct device *dev, if (codec == NULL) return -ENOMEM; - codec->component.dapm_ptr = &codec->dapm; codec->component.codec = codec; ret = snd_soc_component_initialize(&codec->component, @@ -3077,12 +3091,14 @@ int snd_soc_register_codec(struct device *dev, if (codec_drv->read) codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; - codec->dapm.idle_bias_off = codec_drv->idle_bias_off; - codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off; + + dapm = snd_soc_codec_get_dapm(codec); + dapm->idle_bias_off = codec_drv->idle_bias_off; + dapm->suspend_bias_off = codec_drv->suspend_bias_off; if (codec_drv->seq_notifier) - codec->dapm.seq_notifier = codec_drv->seq_notifier; + dapm->seq_notifier = codec_drv->seq_notifier; if (codec_drv->set_bias_level) - codec->dapm.set_bias_level = snd_soc_codec_set_bias_level; + dapm->set_bias_level = snd_soc_codec_set_bias_level; codec->dev = dev; codec->driver = codec_drv; codec->component.val_bytes = codec_drv->reg_word_size; @@ -3129,7 +3145,7 @@ EXPORT_SYMBOL_GPL(snd_soc_register_codec); /** * snd_soc_unregister_codec - Unregister a codec from the ASoC core * - * @codec: codec to unregister + * @dev: codec to unregister */ void snd_soc_unregister_codec(struct device *dev) { @@ -3304,6 +3320,26 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); +void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, + struct snd_soc_codec_conf *codec_conf, + struct device_node *of_node, + const char *propname) +{ + struct device_node *np = card->dev->of_node; + const char *str; + int ret; + + ret = of_property_read_string(np, propname, &str); + if (ret < 0) { + /* no prefix is not error */ + return; + } + + codec_conf->of_node = of_node; + codec_conf->name_prefix = str; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); + int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e0de807..f4bf21a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -47,6 +47,13 @@ #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; +#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ + SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN) + +#define snd_soc_dapm_for_each_direction(dir) \ + for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ + (dir)++) + static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, const char *control, @@ -167,45 +174,59 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) } /* - * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input - * paths - * @w: The widget for which to invalidate the cached number of input paths - * - * The function resets the cached number of inputs for the specified widget and - * all widgets that can be reached via outgoing paths from the widget. - * - * This function must be called if the number of input paths for a widget might - * have changed. E.g. if the source state of a widget changes or a path is added - * or activated with the widget as the sink. + * Common implementation for dapm_widget_invalidate_input_paths() and + * dapm_widget_invalidate_output_paths(). The function is inlined since the + * combined size of the two specialized functions is only marginally larger then + * the size of the generic function and at the same time the fast path of the + * specialized functions is significantly smaller than the generic function. */ -static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) +static __always_inline void dapm_widget_invalidate_paths( + struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir) { - struct snd_soc_dapm_widget *sink; + enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + struct snd_soc_dapm_widget *node; struct snd_soc_dapm_path *p; LIST_HEAD(list); dapm_assert_locked(w->dapm); - if (w->inputs == -1) + if (w->endpoints[dir] == -1) return; - w->inputs = -1; list_add_tail(&w->work_list, &list); + w->endpoints[dir] = -1; list_for_each_entry(w, &list, work_list) { - list_for_each_entry(p, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_path(w, dir, p) { if (p->is_supply || p->weak || !p->connect) continue; - sink = p->sink; - if (sink->inputs != -1) { - sink->inputs = -1; - list_add_tail(&sink->work_list, &list); + node = p->node[rdir]; + if (node->endpoints[dir] != -1) { + node->endpoints[dir] = -1; + list_add_tail(&node->work_list, &list); } } } } /* + * dapm_widget_invalidate_input_paths() - Invalidate the cached number of + * input paths + * @w: The widget for which to invalidate the cached number of input paths + * + * Resets the cached number of inputs for the specified widget and all widgets + * that can be reached via outcoming paths from the widget. + * + * This function must be called if the number of output paths for a widget might + * have changed. E.g. if the source state of a widget changes or a path is added + * or activated with the widget as the sink. + */ +static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) +{ + dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN); +} + +/* * dapm_widget_invalidate_output_paths() - Invalidate the cached number of * output paths * @w: The widget for which to invalidate the cached number of output paths @@ -219,29 +240,7 @@ static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) */ static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) { - struct snd_soc_dapm_widget *source; - struct snd_soc_dapm_path *p; - LIST_HEAD(list); - - dapm_assert_locked(w->dapm); - - if (w->outputs == -1) - return; - - w->outputs = -1; - list_add_tail(&w->work_list, &list); - - list_for_each_entry(w, &list, work_list) { - list_for_each_entry(p, &w->sources, list_sink) { - if (p->is_supply || p->weak || !p->connect) - continue; - source = p->source; - if (source->outputs != -1) { - source->outputs = -1; - list_add_tail(&source->work_list, &list); - } - } - } + dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT); } /* @@ -270,9 +269,9 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) * endpoints is either connected or disconnected that sum won't change, * so there is no need to re-check the path. */ - if (p->source->inputs != 0) + if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0) dapm_widget_invalidate_input_paths(p->sink); - if (p->sink->outputs != 0) + if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0) dapm_widget_invalidate_output_paths(p->source); } @@ -283,11 +282,11 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) mutex_lock(&card->dapm_mutex); list_for_each_entry(w, &card->widgets, list) { - if (w->is_sink || w->is_source) { + if (w->is_ep) { dapm_mark_dirty(w, "Rechecking endpoints"); - if (w->is_sink) + if (w->is_ep & SND_SOC_DAPM_EP_SINK) dapm_widget_invalidate_output_paths(w); - if (w->is_source) + if (w->is_ep & SND_SOC_DAPM_EP_SOURCE) dapm_widget_invalidate_input_paths(w); } } @@ -894,7 +893,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { /* match name */ - list_for_each_entry(path, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, path) { /* mixer/mux paths name must match control name */ if (path->name != (char *)w->kcontrol_news[i].name) continue; @@ -923,18 +922,18 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) static int dapm_new_mux(struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_context *dapm = w->dapm; + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *path; - struct list_head *paths; const char *type; int ret; switch (w->id) { case snd_soc_dapm_mux: - paths = &w->sources; + dir = SND_SOC_DAPM_DIR_OUT; type = "mux"; break; case snd_soc_dapm_demux: - paths = &w->sinks; + dir = SND_SOC_DAPM_DIR_IN; type = "demux"; break; default: @@ -948,7 +947,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return -EINVAL; } - if (list_empty(paths)) { + if (list_empty(&w->edges[dir])) { dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name); return -EINVAL; } @@ -957,16 +956,9 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) if (ret < 0) return ret; - if (w->id == snd_soc_dapm_mux) { - list_for_each_entry(path, &w->sources, list_sink) { - if (path->name) - dapm_kcontrol_add_path(w->kcontrols[0], path); - } - } else { - list_for_each_entry(path, &w->sinks, list_source) { - if (path->name) - dapm_kcontrol_add_path(w->kcontrols[0], path); - } + snd_soc_dapm_widget_for_each_path(w, dir, path) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); } return 0; @@ -1032,66 +1024,59 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) } } -/* add widget to list if it's not already in the list */ -static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list, - struct snd_soc_dapm_widget *w) +static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, + struct list_head *widgets) { - struct snd_soc_dapm_widget_list *wlist; - int wlistsize, wlistentries, i; - - if (*list == NULL) - return -EINVAL; - - wlist = *list; + struct snd_soc_dapm_widget *w; + struct list_head *it; + unsigned int size = 0; + unsigned int i = 0; - /* is this widget already in the list */ - for (i = 0; i < wlist->num_widgets; i++) { - if (wlist->widgets[i] == w) - return 0; - } + list_for_each(it, widgets) + size++; - /* allocate some new space */ - wlistentries = wlist->num_widgets + 1; - wlistsize = sizeof(struct snd_soc_dapm_widget_list) + - wlistentries * sizeof(struct snd_soc_dapm_widget *); - *list = krealloc(wlist, wlistsize, GFP_KERNEL); - if (*list == NULL) { - dev_err(w->dapm->dev, "ASoC: can't allocate widget list for %s\n", - w->name); + *list = kzalloc(sizeof(**list) + size * sizeof(*w), GFP_KERNEL); + if (*list == NULL) return -ENOMEM; - } - wlist = *list; - /* insert the widget */ - dev_dbg(w->dapm->dev, "ASoC: added %s in widget list pos %d\n", - w->name, wlist->num_widgets); + list_for_each_entry(w, widgets, work_list) + (*list)->widgets[i++] = w; - wlist->widgets[wlist->num_widgets] = w; - wlist->num_widgets++; - return 1; + (*list)->num_widgets = i; + + return 0; } /* - * Recursively check for a completed path to an active or physically connected - * output widget. Returns number of complete paths. + * Common implementation for is_connected_output_ep() and + * is_connected_input_ep(). The function is inlined since the combined size of + * the two specialized functions is only marginally larger then the size of the + * generic function and at the same time the fast path of the specialized + * functions is significantly smaller than the generic function. */ -static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_widget_list **list) +static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, + struct list_head *list, enum snd_soc_dapm_direction dir, + int (*fn)(struct snd_soc_dapm_widget *, struct list_head *)) { + enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); struct snd_soc_dapm_path *path; int con = 0; - if (widget->outputs >= 0) - return widget->outputs; + if (widget->endpoints[dir] >= 0) + return widget->endpoints[dir]; DAPM_UPDATE_STAT(widget, path_checks); - if (widget->is_sink && widget->connected) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; + /* do we need to add this widget to the list ? */ + if (list) + list_add_tail(&widget->work_list, list); + + if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { + widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); + return widget->endpoints[dir]; } - list_for_each_entry(path, &widget->sinks, list_source) { + snd_soc_dapm_widget_for_each_path(widget, rdir, path) { DAPM_UPDATE_STAT(widget, neighbour_checks); if (path->weak || path->is_supply) @@ -1100,91 +1085,40 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (path->walking) return 1; - trace_snd_soc_dapm_output_path(widget, path); + trace_snd_soc_dapm_path(widget, dir, path); if (path->connect) { path->walking = 1; - - /* do we need to add this widget to the list ? */ - if (list) { - int err; - err = dapm_list_add_widget(list, path->sink); - if (err < 0) { - dev_err(widget->dapm->dev, - "ASoC: could not add widget %s\n", - widget->name); - path->walking = 0; - return con; - } - } - - con += is_connected_output_ep(path->sink, list); - + con += fn(path->node[dir], list); path->walking = 0; } } - widget->outputs = con; + widget->endpoints[dir] = con; return con; } /* * Recursively check for a completed path to an active or physically connected + * output widget. Returns number of complete paths. + */ +static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, + struct list_head *list) +{ + return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, + is_connected_output_ep); +} + +/* + * Recursively check for a completed path to an active or physically connected * input widget. Returns number of complete paths. */ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_widget_list **list) + struct list_head *list) { - struct snd_soc_dapm_path *path; - int con = 0; - - if (widget->inputs >= 0) - return widget->inputs; - - DAPM_UPDATE_STAT(widget, path_checks); - - if (widget->is_source && widget->connected) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - list_for_each_entry(path, &widget->sources, list_sink) { - DAPM_UPDATE_STAT(widget, neighbour_checks); - - if (path->weak || path->is_supply) - continue; - - if (path->walking) - return 1; - - trace_snd_soc_dapm_input_path(widget, path); - - if (path->connect) { - path->walking = 1; - - /* do we need to add this widget to the list ? */ - if (list) { - int err; - err = dapm_list_add_widget(list, path->source); - if (err < 0) { - dev_err(widget->dapm->dev, - "ASoC: could not add widget %s\n", - widget->name); - path->walking = 0; - return con; - } - } - - con += is_connected_input_ep(path->source, list); - - path->walking = 0; - } - } - - widget->inputs = con; - - return con; + return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, + is_connected_input_ep); } /** @@ -1204,7 +1138,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, { struct snd_soc_card *card = dai->component->card; struct snd_soc_dapm_widget *w; + LIST_HEAD(widgets); int paths; + int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); @@ -1213,14 +1149,21 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, * to reset the cached number of inputs and outputs. */ list_for_each_entry(w, &card->widgets, list) { - w->inputs = -1; - w->outputs = -1; + w->endpoints[SND_SOC_DAPM_DIR_IN] = -1; + w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1; } if (stream == SNDRV_PCM_STREAM_PLAYBACK) - paths = is_connected_output_ep(dai->playback_widget, list); + paths = is_connected_output_ep(dai->playback_widget, &widgets); else - paths = is_connected_input_ep(dai->capture_widget, list); + paths = is_connected_input_ep(dai->capture_widget, &widgets); + + /* Drop starting point */ + list_del(widgets.next); + + ret = dapm_widget_list_create(list, &widgets); + if (ret) + paths = ret; trace_snd_soc_dapm_connected(paths, stream); mutex_unlock(&card->dapm_mutex); @@ -1321,7 +1264,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); /* Check if one of our outputs is connected */ - list_for_each_entry(path, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, path) { DAPM_UPDATE_STAT(w, neighbour_checks); if (path->weak) @@ -1745,12 +1688,12 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, /* If we changed our power state perhaps our neigbours changed * also. */ - list_for_each_entry(path, &w->sources, list_sink) + snd_soc_dapm_widget_for_each_source_path(w, path) dapm_widget_set_peer_power(path->source, power, path->connect); /* Supplies can't affect their outputs, only their inputs */ if (!w->is_supply) { - list_for_each_entry(path, &w->sinks, list_source) + snd_soc_dapm_widget_for_each_sink_path(w, path) dapm_widget_set_peer_power(path->sink, power, path->connect); } @@ -1951,6 +1894,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, { struct snd_soc_dapm_widget *w = file->private_data; struct snd_soc_card *card = w->dapm->card; + enum snd_soc_dapm_direction dir, rdir; char *buf; int in, out; ssize_t ret; @@ -1987,25 +1931,21 @@ static ssize_t dapm_widget_power_read_file(struct file *file, w->sname, w->active ? "active" : "inactive"); - list_for_each_entry(p, &w->sources, list_sink) { - if (p->connected && !p->connected(w, p->source)) - continue; + snd_soc_dapm_for_each_direction(dir) { + rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + snd_soc_dapm_widget_for_each_path(w, dir, p) { + if (p->connected && !p->connected(w, p->node[rdir])) + continue; - if (p->connect) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - " in \"%s\" \"%s\"\n", - p->name ? p->name : "static", - p->source->name); - } - list_for_each_entry(p, &w->sinks, list_source) { - if (p->connected && !p->connected(w, p->sink)) - continue; + if (!p->connect) + continue; - if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " out \"%s\" \"%s\"\n", + " %s \"%s\" \"%s\"\n", + (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out", p->name ? p->name : "static", - p->sink->name); + p->node[rdir]->name); + } } mutex_unlock(&card->dapm_mutex); @@ -2223,14 +2163,16 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); -static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf) +static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, + char *buf) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); struct snd_soc_dapm_widget *w; int count = 0; char *state = "not set"; - list_for_each_entry(w, &codec->component.card->widgets, list) { - if (w->dapm != &codec->dapm) + list_for_each_entry(w, &cmpnt->card->widgets, list) { + if (w->dapm != dapm) continue; /* only display widgets that burnm power */ @@ -2258,7 +2200,7 @@ static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf) } } - switch (codec->dapm.bias_level) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_ON: state = "On"; break; @@ -2287,8 +2229,9 @@ static ssize_t dapm_widget_show(struct device *dev, mutex_lock(&rtd->card->dapm_mutex); for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_codec *codec = rtd->codec_dais[i]->codec; - count += dapm_widget_show_codec(codec, buf + count); + struct snd_soc_component *cmpnt = rtd->codec_dais[i]->component; + + count += dapm_widget_show_component(cmpnt, buf + count); } mutex_unlock(&rtd->card->dapm_mutex); @@ -2305,37 +2248,43 @@ struct attribute *soc_dapm_dev_attrs[] = { static void dapm_free_path(struct snd_soc_dapm_path *path) { - list_del(&path->list_sink); - list_del(&path->list_source); + list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]); + list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]); list_del(&path->list_kcontrol); list_del(&path->list); kfree(path); } +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p, *next_p; + enum snd_soc_dapm_direction dir; + + list_del(&w->list); + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + snd_soc_dapm_for_each_direction(dir) { + snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) + dapm_free_path(p); + } + + kfree(w->kcontrols); + kfree_const(w->name); + kfree(w); +} + /* free all dapm widgets and resources */ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w, *next_w; - struct snd_soc_dapm_path *p, *next_p; list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { if (w->dapm != dapm) continue; - list_del(&w->list); - /* - * remove source and sink paths associated to this widget. - * While removing the path, remove reference to it from both - * source and sink widgets so that path is removed only once. - */ - list_for_each_entry_safe(p, next_p, &w->sources, list_sink) - dapm_free_path(p); - - list_for_each_entry_safe(p, next_p, &w->sinks, list_source) - dapm_free_path(p); - - kfree(w->kcontrols); - kfree(w->name); - kfree(w); + snd_soc_dapm_free_widget(w); } } @@ -2441,20 +2390,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); */ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) { + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *p; + unsigned int ep; switch (w->id) { case snd_soc_dapm_input: /* On a fully routed card a input is never a source */ if (w->dapm->card->fully_routed) - break; - w->is_source = 1; - list_for_each_entry(p, &w->sources, list_sink) { + return; + ep = SND_SOC_DAPM_EP_SOURCE; + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->source->id == snd_soc_dapm_micbias || p->source->id == snd_soc_dapm_mic || p->source->id == snd_soc_dapm_line || p->source->id == snd_soc_dapm_output) { - w->is_source = 0; + ep = 0; break; } } @@ -2462,25 +2413,30 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) case snd_soc_dapm_output: /* On a fully routed card a output is never a sink */ if (w->dapm->card->fully_routed) - break; - w->is_sink = 1; - list_for_each_entry(p, &w->sinks, list_source) { + return; + ep = SND_SOC_DAPM_EP_SINK; + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->sink->id == snd_soc_dapm_spk || p->sink->id == snd_soc_dapm_hp || p->sink->id == snd_soc_dapm_line || p->sink->id == snd_soc_dapm_input) { - w->is_sink = 0; + ep = 0; break; } } break; case snd_soc_dapm_line: - w->is_sink = !list_empty(&w->sources); - w->is_source = !list_empty(&w->sinks); + ep = 0; + snd_soc_dapm_for_each_direction(dir) { + if (!list_empty(&w->edges[dir])) + ep |= SND_SOC_DAPM_DIR_TO_EP(dir); + } break; default: - break; + return; } + + w->is_ep = ep; } static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, @@ -2533,6 +2489,8 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink)) { + struct snd_soc_dapm_widget *widgets[2]; + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *path; int ret; @@ -2565,13 +2523,14 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, if (!path) return -ENOMEM; - path->source = wsource; - path->sink = wsink; + path->node[SND_SOC_DAPM_DIR_IN] = wsource; + path->node[SND_SOC_DAPM_DIR_OUT] = wsink; + widgets[SND_SOC_DAPM_DIR_IN] = wsource; + widgets[SND_SOC_DAPM_DIR_OUT] = wsink; + path->connected = connected; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_kcontrol); - INIT_LIST_HEAD(&path->list_source); - INIT_LIST_HEAD(&path->list_sink); if (wsource->is_supply || wsink->is_supply) path->is_supply = 1; @@ -2609,14 +2568,13 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, } list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); - - dapm_update_widget_flags(wsource); - dapm_update_widget_flags(wsink); + snd_soc_dapm_for_each_direction(dir) + list_add(&path->list_node[dir], &widgets[dir]->edges[dir]); - dapm_mark_dirty(wsource, "Route added"); - dapm_mark_dirty(wsink, "Route added"); + snd_soc_dapm_for_each_direction(dir) { + dapm_update_widget_flags(widgets[dir]); + dapm_mark_dirty(widgets[dir], "Route added"); + } if (dapm->card->instantiated && path->connect) dapm_path_invalidate(path); @@ -2864,7 +2822,7 @@ static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", route->source, route->sink); - list_for_each_entry(path, &source->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(source, path) { if (path->sink == sink) { path->weak = 1; count++; @@ -2918,7 +2876,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); /** * snd_soc_dapm_new_widgets - add new dapm widgets - * @dapm: DAPM context + * @card: card to be checked for new dapm widgets * * Checks the codec for any new dapm widgets and creates them if found. * @@ -3298,6 +3256,7 @@ struct snd_soc_dapm_widget * snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) { + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_widget *w; const char *prefix; int ret; @@ -3344,7 +3303,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, if (prefix) w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); else - w->name = kasprintf(GFP_KERNEL, "%s", widget->name); + w->name = kstrdup_const(widget->name, GFP_KERNEL); if (w->name == NULL) { kfree(w); return NULL; @@ -3352,27 +3311,27 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, switch (w->id) { case snd_soc_dapm_mic: - w->is_source = 1; + w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_input: if (!dapm->card->fully_routed) - w->is_source = 1; + w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_spk: case snd_soc_dapm_hp: - w->is_sink = 1; + w->is_ep = SND_SOC_DAPM_EP_SINK; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_output: if (!dapm->card->fully_routed) - w->is_sink = 1; + w->is_ep = SND_SOC_DAPM_EP_SINK; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_vmid: case snd_soc_dapm_siggen: - w->is_source = 1; + w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_always_on_check_power; break; case snd_soc_dapm_mux: @@ -3406,14 +3365,14 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, } w->dapm = dapm; - INIT_LIST_HEAD(&w->sources); - INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->dirty); list_add_tail(&w->list, &dapm->card->widgets); - w->inputs = -1; - w->outputs = -1; + snd_soc_dapm_for_each_direction(dir) { + INIT_LIST_HEAD(&w->edges[dir]); + w->endpoints[dir] = -1; + } /* machine layer set ups unconnected pins and insertions */ w->connected = 1; @@ -3467,19 +3426,17 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, int ret; if (WARN_ON(!config) || - WARN_ON(list_empty(&w->sources) || list_empty(&w->sinks))) + WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || + list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) return -EINVAL; /* We only support a single source and sink, pick the first */ - source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path, - list_sink); - sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, - list_source); - - if (WARN_ON(!source_p || !sink_p) || - WARN_ON(!sink_p->source || !source_p->sink) || - WARN_ON(!source_p->source || !sink_p->sink)) - return -EINVAL; + source_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_OUT], + struct snd_soc_dapm_path, + list_node[SND_SOC_DAPM_DIR_OUT]); + sink_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_IN], + struct snd_soc_dapm_path, + list_node[SND_SOC_DAPM_DIR_IN]); source = source_p->source->priv; sink = sink_p->sink->priv; @@ -3821,11 +3778,6 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, for (i = 0; i < rtd->num_codecs; i++) { struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - /* there is no point in connecting BE DAI links with dummies */ - if (snd_soc_dai_is_dummy(codec_dai) || - snd_soc_dai_is_dummy(cpu_dai)) - continue; - /* connect BE DAI playback if widgets are valid */ if (codec_dai->playback_widget && cpu_dai->playback_widget) { source = cpu_dai->playback_widget; @@ -3856,6 +3808,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event) { struct snd_soc_dapm_widget *w; + unsigned int ep; if (stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; @@ -3865,12 +3818,22 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, if (w) { dapm_mark_dirty(w, "stream event"); + if (w->id == snd_soc_dapm_dai_in) { + ep = SND_SOC_DAPM_EP_SOURCE; + dapm_widget_invalidate_input_paths(w); + } else { + ep = SND_SOC_DAPM_EP_SINK; + dapm_widget_invalidate_output_paths(w); + } + switch (event) { case SND_SOC_DAPM_STREAM_START: w->active = 1; + w->is_ep = ep; break; case SND_SOC_DAPM_STREAM_STOP: w->active = 0; + w->is_ep = 0; break; case SND_SOC_DAPM_STREAM_SUSPEND: case SND_SOC_DAPM_STREAM_RESUME: @@ -3878,14 +3841,6 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: break; } - - if (w->id == snd_soc_dapm_dai_in) { - w->is_source = w->active; - dapm_widget_invalidate_input_paths(w); - } else { - w->is_sink = w->active; - dapm_widget_invalidate_output_paths(w); - } } } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 256b9c9..70e4b9d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1231,24 +1231,17 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, } int dpcm_path_get(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list_) + int stream, struct snd_soc_dapm_widget_list **list) { struct snd_soc_dai *cpu_dai = fe->cpu_dai; - struct snd_soc_dapm_widget_list *list; int paths; - list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) + - sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL); - if (list == NULL) - return -ENOMEM; - /* get number of valid DAI paths and their widgets */ - paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); + paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list); dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, stream ? "capture" : "playback"); - *list_ = list; return paths; } @@ -1306,7 +1299,12 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, switch (list->widgets[i]->id) { case snd_soc_dapm_dai_in: + if (stream != SNDRV_PCM_STREAM_PLAYBACK) + continue; + break; case snd_soc_dapm_dai_out: + if (stream != SNDRV_PCM_STREAM_CAPTURE) + continue; break; default: continue; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 31068b8..69d01cd 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -45,12 +45,12 @@ #define SOC_TPLG_PASS_VENDOR 1 #define SOC_TPLG_PASS_MIXER 2 #define SOC_TPLG_PASS_WIDGET 3 -#define SOC_TPLG_PASS_GRAPH 4 -#define SOC_TPLG_PASS_PINS 5 -#define SOC_TPLG_PASS_PCM_DAI 6 +#define SOC_TPLG_PASS_PCM_DAI 4 +#define SOC_TPLG_PASS_GRAPH 5 +#define SOC_TPLG_PASS_PINS 6 #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST -#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PCM_DAI +#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS struct soc_tplg { const struct firmware *fw; @@ -66,10 +66,14 @@ struct soc_tplg { u32 index; /* current block index */ u32 req_index; /* required index, only loaded/free matching blocks */ - /* kcontrol operations */ + /* vendor specific kcontrol operations */ const struct snd_soc_tplg_kcontrol_ops *io_ops; int io_ops_count; + /* vendor specific bytes ext handlers, for TLV bytes controls */ + const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops; + int bytes_ext_ops_count; + /* optional fw loading callbacks to component drivers */ struct snd_soc_tplg_ops *ops; }; @@ -508,38 +512,74 @@ static void remove_pcm_dai(struct snd_soc_component *comp, /* bind a kcontrol to it's IO handlers */ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, struct snd_kcontrol_new *k, - const struct snd_soc_tplg_kcontrol_ops *ops, int num_ops, - const struct snd_soc_tplg_kcontrol_ops *bops, int num_bops) + const struct soc_tplg *tplg) { - int i; + const struct snd_soc_tplg_kcontrol_ops *ops; + const struct snd_soc_tplg_bytes_ext_ops *ext_ops; + int num_ops, i; + + if (hdr->ops.info == SND_SOC_TPLG_CTL_BYTES + && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER + && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE + && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + struct soc_bytes_ext *sbe; + struct snd_soc_tplg_bytes_control *be; + + sbe = (struct soc_bytes_ext *)k->private_value; + be = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); + + /* TLV bytes controls need standard kcontrol info handler, + * TLV callback and extended put/get handlers. + */ + k->info = snd_soc_bytes_info; + k->tlv.c = snd_soc_bytes_tlv_callback; + + ext_ops = tplg->bytes_ext_ops; + num_ops = tplg->bytes_ext_ops_count; + for (i = 0; i < num_ops; i++) { + if (!sbe->put && ext_ops[i].id == be->ext_ops.put) + sbe->put = ext_ops[i].put; + if (!sbe->get && ext_ops[i].id == be->ext_ops.get) + sbe->get = ext_ops[i].get; + } - /* try and map standard kcontrols handler first */ + if (sbe->put && sbe->get) + return 0; + else + return -EINVAL; + } + + /* try and map vendor specific kcontrol handlers first */ + ops = tplg->io_ops; + num_ops = tplg->io_ops_count; for (i = 0; i < num_ops; i++) { - if (ops[i].id == hdr->ops.put) + if (k->put == NULL && ops[i].id == hdr->ops.put) k->put = ops[i].put; - if (ops[i].id == hdr->ops.get) + if (k->get == NULL && ops[i].id == hdr->ops.get) k->get = ops[i].get; - if (ops[i].id == hdr->ops.info) + if (k->info == NULL && ops[i].id == hdr->ops.info) k->info = ops[i].info; } - /* standard handlers found ? */ + /* vendor specific handlers found ? */ if (k->put && k->get && k->info) return 0; - /* none found so try bespoke handlers */ - for (i = 0; i < num_bops; i++) { + /* none found so try standard kcontrol handlers */ + ops = io_ops; + num_ops = ARRAY_SIZE(io_ops); + for (i = 0; i < num_ops; i++) { - if (k->put == NULL && bops[i].id == hdr->ops.put) - k->put = bops[i].put; - if (k->get == NULL && bops[i].id == hdr->ops.get) - k->get = bops[i].get; - if (k->info == NULL && bops[i].id == hdr->ops.info) - k->info = bops[i].info; + if (k->put == NULL && ops[i].id == hdr->ops.put) + k->put = ops[i].put; + if (k->get == NULL && ops[i].id == hdr->ops.get) + k->get = ops[i].get; + if (k->info == NULL && ops[i].id == hdr->ops.info) + k->info = ops[i].info; } - /* bespoke handlers found ? */ + /* standard handlers found ? */ if (k->put && k->get && k->info) return 0; @@ -609,9 +649,7 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg, if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) return 0; - if (tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - kc->tlv.c = snd_soc_bytes_tlv_callback; - } else { + if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { tplg_tlv = &tc->tlv; switch (tplg_tlv->type) { case SNDRV_CTL_TLVT_DB_SCALE: @@ -682,8 +720,7 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, INIT_LIST_HEAD(&sbe->dobj.list); /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); if (err) { soc_control_err(tplg, &be->hdr, be->hdr.name); kfree(sbe); @@ -777,8 +814,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); if (err) { soc_control_err(tplg, &mc->hdr, mc->hdr.name); kfree(sm); @@ -855,12 +891,12 @@ static int soc_tplg_denum_create_values(struct soc_enum *se, if (ec->items > sizeof(*ec->values)) return -EINVAL; - se->dobj.control.dvalues = - kmalloc(ec->items * sizeof(u32), GFP_KERNEL); + se->dobj.control.dvalues = kmemdup(ec->values, + ec->items * sizeof(u32), + GFP_KERNEL); if (!se->dobj.control.dvalues) return -ENOMEM; - memcpy(se->dobj.control.dvalues, ec->values, ec->items * sizeof(u32)); return 0; } @@ -950,8 +986,7 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, } /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); if (err) { soc_control_err(tplg, &ec->hdr, ec->hdr.name); kfree(se); @@ -1093,7 +1128,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( struct snd_soc_tplg_mixer_control *mc; int i, err; - kc = kzalloc(sizeof(*kc) * num_kcontrols, GFP_KERNEL); + kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); if (kc == NULL) return NULL; @@ -1137,8 +1172,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], tplg); if (err) { soc_control_err(tplg, &mc->hdr, mc->hdr.name); kfree(sm); @@ -1235,8 +1269,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( } /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); if (err) { soc_control_err(tplg, &ec->hdr, ec->hdr.name); goto err_se; @@ -1274,7 +1307,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( struct snd_kcontrol_new *kc; int i, err; - kc = kzalloc(sizeof(*kc) * count, GFP_KERNEL); + kc = kcalloc(count, sizeof(*kc), GFP_KERNEL); if (!kc) return NULL; @@ -1297,7 +1330,6 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( "ASoC: adding bytes kcontrol %s with access 0x%x\n", be->hdr.name, be->hdr.access); - memset(kc, 0, sizeof(*kc)); kc[i].name = be->hdr.name; kc[i].private_value = (long)sbe; kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -1307,9 +1339,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( INIT_LIST_HEAD(&sbe->dobj.list); /* map standard io handlers and check for external handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, - tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], tplg); if (err) { soc_control_err(tplg, &be->hdr, be->hdr.name); kfree(sbe); @@ -1737,6 +1767,8 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp, tplg.req_index = id; tplg.io_ops = ops->io_ops; tplg.io_ops_count = ops->io_ops_count; + tplg.bytes_ext_ops = ops->bytes_ext_ops; + tplg.bytes_ext_ops_count = ops->bytes_ext_ops_count; return soc_tplg_load(&tplg); } @@ -1758,7 +1790,6 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, u32 index) { struct snd_soc_dapm_widget *w, *next_w; - struct snd_soc_dapm_path *p, *next_p; list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { @@ -1770,31 +1801,9 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, if (w->dobj.index != index && w->dobj.index != SND_SOC_TPLG_INDEX_ALL) continue; - - list_del(&w->list); - - /* - * remove source and sink paths associated to this widget. - * While removing the path, remove reference to it from both - * source and sink widgets so that path is removed only once. - */ - list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { - list_del(&p->list_sink); - list_del(&p->list_source); - list_del(&p->list); - kfree(p); - } - list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { - list_del(&p->list_sink); - list_del(&p->list_source); - list_del(&p->list); - kfree(p); - } /* check and free and dynamic widget kcontrols */ snd_soc_tplg_widget_remove(w); - kfree(w->kcontrols); - kfree(w->name); - kfree(w); + snd_soc_dapm_free_widget(w); } } EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index a402860..977a078 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -203,35 +203,25 @@ static int spdif_in_probe(struct platform_device *pdev) struct spdif_in_dev *host; struct spear_spdif_platform_data *pdata; struct resource *res, *res_fifo; + void __iomem *io_base; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; + io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res_fifo) return -EINVAL; - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_warn(&pdev->dev, "Failed to get memory resourse\n"); - return -ENOENT; - } - host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { dev_warn(&pdev->dev, "kzalloc fail\n"); return -ENOMEM; } - host->io_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!host->io_base) { - dev_warn(&pdev->dev, "ioremap failed\n"); - return -ENOMEM; - } - + host->io_base = io_base; host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) return -EINVAL; diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c index a7dc3c5..e8476da 100644 --- a/sound/soc/spear/spear_pcm.c +++ b/sound/soc/spear/spear_pcm.c @@ -44,7 +44,7 @@ int devm_spear_pcm_platform_register(struct device *dev, *config = spear_dmaengine_pcm_config; config->compat_filter_fn = filter; - return snd_dmaengine_pcm_register(dev, config, + return devm_snd_dmaengine_pcm_register(dev, config, SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig new file mode 100644 index 0000000..64a6900 --- /dev/null +++ b/sound/soc/sti/Kconfig @@ -0,0 +1,11 @@ +# +# STM SoC audio configuration +# +menuconfig SND_SOC_STI + tristate "SoC Audio support for STI System-On-Chip" + depends on SND_SOC + depends on ARCH_STI || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y if you want to enable ASoC-support for + any of the STI platforms (e.g. STIH416). diff --git a/sound/soc/sti/Makefile b/sound/soc/sti/Makefile new file mode 100644 index 0000000..4b188d2 --- /dev/null +++ b/sound/soc/sti/Makefile @@ -0,0 +1,4 @@ +# STI platform support +snd-soc-sti-objs := sti_uniperif.o uniperif_player.o uniperif_reader.o + +obj-$(CONFIG_SND_SOC_STI) += snd-soc-sti.o diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c new file mode 100644 index 0000000..39bcefe --- /dev/null +++ b/sound/soc/sti/sti_uniperif.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> + +#include "uniperif.h" + +/* + * sti_uniperiph_dai_create_ctrl + * This function is used to create Ctrl associated to DAI but also pcm device. + * Request is done by front end to associate ctrl with pcm device id + */ +static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *uni = priv->dai_data.uni; + struct snd_kcontrol_new *ctrl; + int i; + + if (!uni->num_ctrls) + return 0; + + for (i = 0; i < uni->num_ctrls; i++) { + /* + * Several Control can have same name. Controls are indexed on + * Uniperipheral instance ID + */ + ctrl = &uni->snd_ctrls[i]; + ctrl->index = uni->info->id; + ctrl->device = uni->info->id; + } + + return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls); +} + +/* + * DAI + */ +int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_dmaengine_dai_dma_data *dma_data; + int transfer_size; + + transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES; + + dma_data = snd_soc_dai_get_dma_data(dai, substream); + dma_data->maxburst = transfer_size; + + return 0; +} + +int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + + priv->dai_data.uni->daifmt = fmt; + + return 0; +} + +static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *uni = priv->dai_data.uni; + int ret; + + /* The uniperipheral should be in stopped state */ + if (uni->state != UNIPERIF_STATE_STOPPED) { + dev_err(uni->dev, "%s: invalid uni state( %d)", + __func__, (int)uni->state); + return -EBUSY; + } + + /* Pinctrl: switch pinstate to sleep */ + ret = pinctrl_pm_select_sleep_state(uni->dev); + if (ret) + dev_err(uni->dev, "%s: failed to select pinctrl state", + __func__); + + return ret; +} + +static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *uni = priv->dai_data.uni; + int ret; + + if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) { + ret = uni_player_resume(uni); + if (ret) + return ret; + } + + /* pinctrl: switch pinstate to default */ + ret = pinctrl_pm_select_default_state(uni->dev); + if (ret) + dev_err(uni->dev, "%s: failed to select pinctrl state", + __func__); + + return ret; +} + +static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_uniperiph_dai *dai_data = &priv->dai_data; + + /* DMA settings*/ + if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) + snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL); + else + snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data); + + dai_data->dma_data.addr = dai_data->uni->fifo_phys_address; + dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + return sti_uniperiph_dai_create_ctrl(dai); +} + +static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { + .probe = sti_uniperiph_dai_probe, + .suspend = sti_uniperiph_dai_suspend, + .resume = sti_uniperiph_dai_resume +}; + +static const struct snd_soc_component_driver sti_uniperiph_dai_component = { + .name = "sti_cpu_dai", +}; + +static int sti_uniperiph_cpu_dai_of(struct device_node *node, + struct sti_uniperiph_data *priv) +{ + const char *str; + int ret; + struct device *dev = &priv->pdev->dev; + struct sti_uniperiph_dai *dai_data = &priv->dai_data; + struct snd_soc_dai_driver *dai = priv->dai; + struct snd_soc_pcm_stream *stream; + struct uniperif *uni; + + uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL); + if (!uni) + return -ENOMEM; + + *dai = sti_uniperiph_dai_template; + ret = of_property_read_string(node, "dai-name", &str); + if (ret < 0) { + dev_err(dev, "%s: dai name missing.\n", __func__); + return -EINVAL; + } + dai->name = str; + + /* Get resources */ + uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0); + + if (!uni->mem_region) { + dev_err(dev, "Failed to get memory resource"); + return -ENODEV; + } + + uni->base = devm_ioremap_resource(dev, uni->mem_region); + + if (IS_ERR(uni->base)) + return PTR_ERR(uni->base); + + uni->fifo_phys_address = uni->mem_region->start + + UNIPERIF_FIFO_DATA_OFFSET(uni); + + uni->irq = platform_get_irq(priv->pdev, 0); + if (uni->irq < 0) { + dev_err(dev, "Failed to get IRQ resource"); + return -ENXIO; + } + + dai_data->uni = uni; + + if (of_device_is_compatible(node, "st,sti-uni-player")) { + uni_player_init(priv->pdev, uni); + stream = &dai->playback; + } else { + uni_reader_init(priv->pdev, uni); + stream = &dai->capture; + } + dai->ops = uni->dai_ops; + + stream->stream_name = dai->name; + stream->channels_min = uni->hw->channels_min; + stream->channels_max = uni->hw->channels_max; + stream->rates = uni->hw->rates; + stream->formats = uni->hw->formats; + + return 0; +} + +static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = { + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static int sti_uniperiph_probe(struct platform_device *pdev) +{ + struct sti_uniperiph_data *priv; + struct device_node *node = pdev->dev.of_node; + int ret; + + /* Allocate the private data and the CPU_DAI array */ + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL); + if (!priv->dai) + return -ENOMEM; + + priv->pdev = pdev; + + ret = sti_uniperiph_cpu_dai_of(node, priv); + + dev_set_drvdata(&pdev->dev, priv); + + ret = devm_snd_soc_register_component(&pdev->dev, + &sti_uniperiph_dai_component, + priv->dai, 1); + if (ret < 0) + return ret; + + return devm_snd_dmaengine_pcm_register(&pdev->dev, + &dmaengine_pcm_config, 0); +} + +static const struct of_device_id snd_soc_sti_match[] = { + { .compatible = "st,sti-uni-player", }, + { .compatible = "st,sti-uni-reader", }, + {}, +}; + +static struct platform_driver sti_uniperiph_driver = { + .driver = { + .name = "sti-uniperiph-dai", + .of_match_table = snd_soc_sti_match, + }, + .probe = sti_uniperiph_probe, +}; +module_platform_driver(sti_uniperiph_driver); + +MODULE_DESCRIPTION("uniperipheral DAI driver"); +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h new file mode 100644 index 0000000..f0fd5a9 --- /dev/null +++ b/sound/soc/sti/uniperif.h @@ -0,0 +1,1229 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef __SND_ST_AUD_UNIPERIF_H +#define __SND_ST_AUD_UNIPERIF_H + +#include <linux/regmap.h> + +#include <sound/dmaengine_pcm.h> + +/* + * Register access macros + */ + +#define GET_UNIPERIF_REG(ip, offset, shift, mask) \ + ((readl_relaxed(ip->base + offset) >> shift) & mask) +#define SET_UNIPERIF_REG(ip, offset, shift, mask, value) \ + writel_relaxed(((readl_relaxed(ip->base + offset) & \ + ~(mask << shift)) | (((value) & mask) << shift)), ip->base + offset) +#define SET_UNIPERIF_BIT_REG(ip, offset, shift, mask, value) \ + writel_relaxed((((value) & mask) << shift), ip->base + offset) + +/* + * AUD_UNIPERIF_SOFT_RST reg + */ + +#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000 +#define GET_UNIPERIF_SOFT_RST(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + readl_relaxed(ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) : 0) +#define SET_UNIPERIF_SOFT_RST(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) + +/* SOFT_RST */ +#define UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip) 0x0 +#define UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip) 0x1 +#define SET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \ + SET_UNIPERIF_BIT_REG(ip, \ + UNIPERIF_SOFT_RST_OFFSET(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip), 1) +#define GET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_SOFT_RST_OFFSET(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip)) + +/* + * AUD_UNIPERIF_FIFO_DATA reg + */ + +#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004 +#define SET_UNIPERIF_DATA(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip)) + +/* + * AUD_UNIPERIF_CHANNEL_STA_REGN reg + */ + +#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n)) +#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n)) +#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \ + writel_relaxed(value, ip->base + \ + UNIPERIF_CHANNEL_STA_REGN(ip, n)) + +#define UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip) 0x0060 +#define GET_UNIPERIF_CHANNEL_STA_REG0(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG0(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip) 0x0064 +#define GET_UNIPERIF_CHANNEL_STA_REG1(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG1(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip) 0x0068 +#define GET_UNIPERIF_CHANNEL_STA_REG2(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG2(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip) 0x006C +#define GET_UNIPERIF_CHANNEL_STA_REG3(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG3(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip) 0x0070 +#define GET_UNIPERIF_CHANNEL_STA_REG4(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG4(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip) 0x0074 +#define GET_UNIPERIF_CHANNEL_STA_REG5(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG5(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip)) + +/* + * AUD_UNIPERIF_ITS reg + */ + +#define UNIPERIF_ITS_OFFSET(ip) 0x000C +#define GET_UNIPERIF_ITS(ip) \ + readl_relaxed(ip->base + UNIPERIF_ITS_OFFSET(ip)) + +/* MEM_BLK_READ */ +#define UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip) 5 +#define UNIPERIF_ITS_MEM_BLK_READ_MASK(ip) \ + (BIT(UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip))) + +/* FIFO_ERROR */ +#define UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITS_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip))) + +/* DMA_ERROR */ +#define UNIPERIF_ITS_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITS_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_DMA_ERROR_SHIFT(ip))) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip)))) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip)))) + +/* + * AUD_UNIPERIF_ITS_BCLR reg + */ + +/* FIFO_ERROR */ +#define UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITS_BCLR(ip, \ + UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip)) + +#define UNIPERIF_ITS_BCLR_OFFSET(ip) 0x0010 +#define SET_UNIPERIF_ITS_BCLR(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip)) + +/* + * AUD_UNIPERIF_ITM reg + */ + +#define UNIPERIF_ITM_OFFSET(ip) 0x0018 +#define GET_UNIPERIF_ITM(ip) \ + readl_relaxed(ip->base + UNIPERIF_ITM_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip))) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip)))) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip)))) + +/* + * AUD_UNIPERIF_ITM_BCLR reg + */ + +#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c +#define SET_UNIPERIF_ITM_BCLR(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITM_BCLR_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITM_BCLR(ip, \ + UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip)) + +/* DMA_ERROR */ +#define UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BCLR_DMA_ERROR(ip) \ + SET_UNIPERIF_ITM_BCLR(ip, \ + UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip)) + +/* + * AUD_UNIPERIF_ITM_BSET reg + */ + +#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020 +#define SET_UNIPERIF_ITM_BSET(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITM_BSET_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip)) + +/* MEM_BLK_READ */ +#define UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip) 5 +#define UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip)) + +/* DMA_ERROR */ +#define UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_DMA_ERROR(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip)) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip)))) +#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip)) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip)))) +#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip)) + +/* + * UNIPERIF_CONFIG reg + */ + +#define UNIPERIF_CONFIG_OFFSET(ip) 0x0040 +#define GET_UNIPERIF_CONFIG(ip) \ + readl_relaxed(ip->base + UNIPERIF_CONFIG_OFFSET(ip)) +#define SET_UNIPERIF_CONFIG(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CONFIG_OFFSET(ip)) + +/* PARITY_CNTR */ +#define UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip) 0 +#define UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_PARITY_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 1) + +/* CHANNEL_STA_CNTR */ +#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip) 1 +#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 1) + +/* USER_DAT_CNTR */ +#define UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip) 2 +#define UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_USER_DAT_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 0) + +/* VALIDITY_DAT_CNTR */ +#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip) 3 +#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 1) + +/* ONE_BIT_AUD_SUPPORT */ +#define UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip) 4 +#define UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_ONE_BIT_AUD(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip)) +#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 1) + +/* MEMORY_FMT */ +#define UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip) 5 +#define UNIPERIF_CONFIG_MEM_FMT_MASK(ip) 0x1 +#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) 0 +#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) 1 +#define GET_UNIPERIF_CONFIG_MEM_FMT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \ + UNIPERIF_CONFIG_MEM_FMT_MASK(ip)) +#define SET_UNIPERIF_CONFIG_MEM_FMT(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \ + UNIPERIF_CONFIG_MEM_FMT_MASK(ip), value) +#define SET_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) \ + SET_UNIPERIF_CONFIG_MEM_FMT(ip, \ + VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip)) +#define SET_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) \ + SET_UNIPERIF_CONFIG_MEM_FMT(ip, \ + VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip)) + +/* REPEAT_CHL_STS */ +#define UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip) 6 +#define UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_REPEAT_CHL_STS(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip)) +#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 1) + +/* BACK_STALL_REQ */ +#define UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 7 : -1) +#define UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_BACK_STALL_REQ(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip)) +#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 1) + +/* FDMA_TRIGGER_LIMIT */ +#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip) 8 +#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip) 0x7F +#define GET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip)) +#define SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip), value) + +/* CHL_STS_UPDATE */ +#define UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1) +#define UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip)) +#define SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip), 1) + +/* IDLE_MOD */ +#define UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip) 18 +#define UNIPERIF_CONFIG_IDLE_MOD_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_IDLE_MOD(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip)) +#define SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_IDLE_MOD_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 1) + +/* SUBFRAME_SELECTION */ +#define UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip) 19 +#define UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_SUBFRAME_SEL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip)) +#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF0_SUBF1(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 0) + +/* FULL_SW_CONTROL */ +#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip) 20 +#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_SPDIF_SW_CTRL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip)) +#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 0) + +/* MASTER_CLKEDGE */ +#define UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 24 : -1) +#define UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_MSTR_CLKEDGE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip)) +#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_FALLING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_RISING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 0) + +/* + * UNIPERIF_CTRL reg + */ + +#define UNIPERIF_CTRL_OFFSET(ip) 0x0044 +#define GET_UNIPERIF_CTRL(ip) \ + readl_relaxed(ip->base + UNIPERIF_CTRL_OFFSET(ip)) +#define SET_UNIPERIF_CTRL(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CTRL_OFFSET(ip)) + +/* OPERATION */ +#define UNIPERIF_CTRL_OPERATION_SHIFT(ip) 0 +#define UNIPERIF_CTRL_OPERATION_MASK(ip) 0x7 +#define GET_UNIPERIF_CTRL_OPERATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip) 0 +#define SET_UNIPERIF_CTRL_OPERATION_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 1 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 2 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) 3 +#define SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip)) +/* This is the same as above! */ +#define VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) 3 +#define SET_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) 4 +#define SET_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 5 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 7) +#define SET_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip)) + +/* EXIT_STBY_ON_EOBLOCK */ +#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 3) +#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip)) +#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 1) + +/* ROUNDING */ +#define UNIPERIF_CTRL_ROUNDING_SHIFT(ip) 4 +#define UNIPERIF_CTRL_ROUNDING_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_ROUNDING(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip)) +#define SET_UNIPERIF_CTRL_ROUNDING_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_ROUNDING_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip), 1) + +/* DIVIDER */ +#define UNIPERIF_CTRL_DIVIDER_SHIFT(ip) 5 +#define UNIPERIF_CTRL_DIVIDER_MASK(ip) 0xff +#define GET_UNIPERIF_CTRL_DIVIDER(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \ + UNIPERIF_CTRL_DIVIDER_MASK(ip)) +#define SET_UNIPERIF_CTRL_DIVIDER(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \ + UNIPERIF_CTRL_DIVIDER_MASK(ip), value) + +/* BYTE_SWAP */ +#define UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 13 : -1) +#define UNIPERIF_CTRL_BYTE_SWP_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_BYTE_SWP(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip)) +#define SET_UNIPERIF_CTRL_BYTE_SWP_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_BYTE_SWP_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 1) + +/* ZERO_STUFFING_HW_SW */ +#define UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 14 : -1) +#define UNIPERIF_CTRL_ZERO_STUFF_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_ZERO_STUFF(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip)) +#define SET_UNIPERIF_CTRL_ZERO_STUFF_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_ZERO_STUFF_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 0) + +/* SPDIF_LAT */ +#define UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1) +#define UNIPERIF_CTRL_SPDIF_LAT_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_SPDIF_LAT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip)) +#define SET_UNIPERIF_CTRL_SPDIF_LAT_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 0) + +/* EN_SPDIF_FORMATTING */ +#define UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip) 17 +#define UNIPERIF_CTRL_SPDIF_FMT_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_SPDIF_FMT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip)) +#define SET_UNIPERIF_CTRL_SPDIF_FMT_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 0) + +/* READER_OUT_SELECT */ +#define UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 18 : -1) +#define UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_READER_OUT_SEL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip)) +#define SET_UNIPERIF_CTRL_READER_OUT_SEL_IN_MEM(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_READER_OUT_SEL_ON_I2S_LINE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + CORAUD_UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1) + +/* UNDERFLOW_REC_WINDOW */ +#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20 +#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip) 0xff +#define GET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip)) +#define SET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip), value) + +/* + * UNIPERIF_I2S_FMT a.k.a UNIPERIF_FORMAT reg + */ + +#define UNIPERIF_I2S_FMT_OFFSET(ip) 0x0048 +#define GET_UNIPERIF_I2S_FMT(ip) \ + readl_relaxed(ip->base + UNIPERIF_I2S_FMT_OFFSET(ip)) +#define SET_UNIPERIF_I2S_FMT(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_I2S_FMT_OFFSET(ip)) + +/* NBIT */ +#define UNIPERIF_I2S_FMT_NBIT_SHIFT(ip) 0 +#define UNIPERIF_I2S_FMT_NBIT_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_NBIT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NBIT_32(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_NBIT_16(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip), 1) + +/* DATA_SIZE */ +#define UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip) 1 +#define UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip) 0x7 +#define GET_UNIPERIF_I2S_FMT_DATA_SIZE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_18(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 1) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_20(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 2) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 3) +#define SET_UNIPERIF_I2S_FMTL_DATA_SIZE_28(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 4) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 5) + +/* LR_POL */ +#define UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip) 4 +#define UNIPERIF_I2S_FMT_LR_POL_MASK(ip) 0x1 +#define VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) 0x0 +#define VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_LR_POL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \ + UNIPERIF_I2S_FMT_LR_POL_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_LR_POL(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \ + UNIPERIF_I2S_FMT_LR_POL_MASK(ip), value) +#define SET_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) \ + SET_UNIPERIF_I2S_FMT_LR_POL(ip, \ + VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip)) +#define SET_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) \ + SET_UNIPERIF_I2S_FMT_LR_POL(ip, \ + VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip)) + +/* SCLK_EDGE */ +#define UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip) 5 +#define UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_SCLK_EDGE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 1) + +/* PADDING */ +#define UNIPERIF_I2S_FMT_PADDING_SHIFT(ip) 6 +#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1 +#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1 +#define VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) 0x0 +#define VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_PADDING(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \ + UNIPERIF_I2S_FMT_PADDING_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_PADDING(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \ + UNIPERIF_I2S_FMT_PADDING_MASK(ip), value) +#define SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) \ + SET_UNIPERIF_I2S_FMT_PADDING(ip, \ + VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip)) +#define SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) \ + SET_UNIPERIF_I2S_FMT_PADDING(ip, \ + VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip)) + +/* ALIGN */ +#define UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip) 7 +#define UNIPERIF_I2S_FMT_ALIGN_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_ALIGN(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 1) + +/* ORDER */ +#define UNIPERIF_I2S_FMT_ORDER_SHIFT(ip) 8 +#define UNIPERIF_I2S_FMT_ORDER_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_ORDER(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_ORDER_LSB(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_ORDER_MSB(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip), 1) + +/* NUM_CH */ +#define UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip) 9 +#define UNIPERIF_I2S_FMT_NUM_CH_MASK(ip) 0x7 +#define GET_UNIPERIF_I2S_FMT_NUM_CH(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NUM_CH(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_MASK(ip), value) + +/* NO_OF_SAMPLES_TO_READ */ +#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip) 12 +#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip) 0xfffff +#define GET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip), value) + +/* + * UNIPERIF_BIT_CONTROL reg + */ + +#define UNIPERIF_BIT_CONTROL_OFFSET(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0x004c) +#define GET_UNIPERIF_BIT_CONTROL(ip) \ + readl_relaxed(ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip)) +#define SET_UNIPERIF_BIT_CONTROL(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip)) + +/* CLR_UNDERFLOW_DURATION */ +#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip) 0 +#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip) 0x1 +#define GET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip)) +#define SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip), 1) + +/* CHL_STS_UPDATE */ +#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip) 1 +#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip) 0x1 +#define GET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip)) +#define SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \ + SET_UNIPERIF_BIT_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip), 1) + +/* + * UNIPERIF_STATUS_1 reg + */ + +#define UNIPERIF_STATUS_1_OFFSET(ip) 0x0050 +#define GET_UNIPERIF_STATUS_1(ip) \ + readl_relaxed(ip->base + UNIPERIF_STATUS_1_OFFSET(ip)) +#define SET_UNIPERIF_STATUS_1(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_STATUS_1_OFFSET(ip)) + +/* UNDERFLOW_DURATION */ +#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0) +#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip) 0xff +#define GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_STATUS_1_OFFSET(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip)) +#define SET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_STATUS_1_OFFSET(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value) + +/* + * AUD_UNIPERIF_CHANNEL_STA_REGN reg + */ + +#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n)) +#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n)) +#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \ + writel_relaxed(value, ip->base + \ + UNIPERIF_CHANNEL_STA_REGN(ip, n)) + +/* + * AUD_UNIPERIF_USER_VALIDITY reg + */ + +#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090 +#define GET_UNIPERIF_USER_VALIDITY(ip) \ + readl_relaxed(ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip)) +#define SET_UNIPERIF_USER_VALIDITY(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip)) + +/* VALIDITY_LEFT_AND_RIGHT */ +#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip) 0 +#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip) 0x3 +#define GET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_USER_VALIDITY_OFFSET(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip)) +#define SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_USER_VALIDITY_OFFSET(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip), \ + value ? 0x3 : 0) + +/* + * UNIPERIF_DBG_STANDBY_LEFT_SP reg + */ +#define UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip) 0x0150 +#define UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0) +#define UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 0xFFFFFF) +#define GET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip)) +#define SET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value) + +/* + * uniperipheral IP capabilities + */ + +#define UNIPERIF_FIFO_SIZE 70 /* FIFO is 70 cells deep */ +#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */ + +/* + * Uniperipheral IP revisions + */ +enum uniperif_version { + SND_ST_UNIPERIF_VERSION_UNKNOWN, + /* SASG1 (Orly), Newman */ + SND_ST_UNIPERIF_VERSION_C6AUD0_UNI_1_0, + /* SASC1, SASG2 (Orly2) */ + SND_ST_UNIPERIF_VERSION_UNI_PLR_1_0, + /* SASC1, SASG2 (Orly2), TELSS, Cannes */ + SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0, + /* TELSS (SASC1) */ + SND_ST_UNIPERIF_VERSION_TDM_PLR_1_0, + /* Cannes/Monaco */ + SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 +}; + +enum uniperif_type { + SND_ST_UNIPERIF_PLAYER_TYPE_NONE, + SND_ST_UNIPERIF_PLAYER_TYPE_HDMI, + SND_ST_UNIPERIF_PLAYER_TYPE_PCM, + SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF +}; + +enum uniperif_state { + UNIPERIF_STATE_STOPPED, + UNIPERIF_STATE_STARTED, + UNIPERIF_STATE_STANDBY, + UNIPERIF_STATE_UNDERFLOW, + UNIPERIF_STATE_OVERFLOW = UNIPERIF_STATE_UNDERFLOW, + UNIPERIF_STATE_XRUN +}; + +enum uniperif_iec958_encoding_mode { + UNIPERIF_IEC958_ENCODING_MODE_PCM, + UNIPERIF_IEC958_ENCODING_MODE_ENCODED +}; + +struct uniperif_info { + int id; /* instance value of the uniperipheral IP */ + enum uniperif_type player_type; + int underflow_enabled; /* Underflow recovery mode */ +}; + +struct uniperif_iec958_settings { + enum uniperif_iec958_encoding_mode encoding_mode; + struct snd_aes_iec958 iec958; +}; + +struct uniperif { + /* System information */ + struct uniperif_info *info; + struct device *dev; + int ver; /* IP version, used by register access macros */ + struct regmap_field *clk_sel; + + /* capabilities */ + const struct snd_pcm_hardware *hw; + + /* Resources */ + struct resource *mem_region; + void __iomem *base; + unsigned long fifo_phys_address; + int irq; + + /* Clocks */ + struct clk *clk; + int mclk; + int clk_adj; + + /* Runtime data */ + enum uniperif_state state; + + struct snd_pcm_substream *substream; + + /* Specific to IEC958 player */ + struct uniperif_iec958_settings stream_settings; + struct mutex ctrl_lock; /* For resource updated by stream and controls*/ + + /*alsa ctrl*/ + struct snd_kcontrol_new *snd_ctrls; + int num_ctrls; + + /* dai properties */ + unsigned int daifmt; + + /* DAI callbacks */ + const struct snd_soc_dai_ops *dai_ops; +}; + +struct sti_uniperiph_dai { + int stream; + struct uniperif *uni; + struct snd_dmaengine_dai_dma_data dma_data; +}; + +struct sti_uniperiph_data { + struct platform_device *pdev; + struct snd_soc_dai_driver *dai; + struct sti_uniperiph_dai dai_data; +}; + +/* uniperiph player*/ +int uni_player_init(struct platform_device *pdev, + struct uniperif *uni_player); +int uni_player_resume(struct uniperif *player); + +/* uniperiph reader */ +int uni_reader_init(struct platform_device *pdev, + struct uniperif *uni_reader); + +/* common */ +int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt); + +int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +#endif diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c new file mode 100644 index 0000000..f6eefe1 --- /dev/null +++ b/sound/soc/sti/uniperif_player.c @@ -0,0 +1,1110 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> + +#include <sound/asoundef.h> +#include <sound/soc.h> + +#include "uniperif.h" + +/* + * Some hardware-related definitions + */ + +/* sys config registers definitions */ +#define SYS_CFG_AUDIO_GLUE 0xA4 +#define SYS_CFG_AUDI0_GLUE_PCM_CLKX 8 + +/* + * Driver specific types. + */ +#define UNIPERIF_PLAYER_TYPE_IS_HDMI(p) \ + ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_HDMI) +#define UNIPERIF_PLAYER_TYPE_IS_PCM(p) \ + ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_PCM) +#define UNIPERIF_PLAYER_TYPE_IS_SPDIF(p) \ + ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF) +#define UNIPERIF_PLAYER_TYPE_IS_IEC958(p) \ + (UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \ + UNIPERIF_PLAYER_TYPE_IS_SPDIF(p)) + +#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999 +#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000 + +/* + * Note: snd_pcm_hardware is linked to DMA controller but is declared here to + * integrate DAI_CPU capability in term of rate and supported channels + */ +static const struct snd_pcm_hardware uni_player_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 192000, + + .channels_min = 2, + .channels_max = 8, + + .periods_min = 2, + .periods_max = 48, + + .period_bytes_min = 128, + .period_bytes_max = 64 * PAGE_SIZE, + .buffer_bytes_max = 256 * PAGE_SIZE +}; + +static inline int reset_player(struct uniperif *player) +{ + int count = 10; + + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { + while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) { + udelay(5); + count--; + } + } + + if (!count) { + dev_err(player->dev, "Failed to reset uniperif"); + return -EIO; + } + + return 0; +} + +/* + * uni_player_irq_handler + * In case of error audio stream is stopped; stop action is protected via PCM + * stream lock to avoid race condition with trigger callback. + */ +static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + struct uniperif *player = dev_id; + unsigned int status; + unsigned int tmp; + + if (player->state == UNIPERIF_STATE_STOPPED) { + /* Unexpected IRQ: do nothing */ + return IRQ_NONE; + } + + /* Get interrupt status & clear them immediately */ + status = GET_UNIPERIF_ITS(player); + SET_UNIPERIF_ITS_BCLR(player, status); + + /* Check for fifo error (underrun) */ + if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) { + dev_err(player->dev, "FIFO underflow error detected"); + + /* Interrupt is just for information when underflow recovery */ + if (player->info->underflow_enabled) { + /* Update state to underflow */ + player->state = UNIPERIF_STATE_UNDERFLOW; + + } else { + /* Disable interrupt so doesn't continually fire */ + SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); + + /* Stop the player */ + snd_pcm_stream_lock(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(player->substream); + } + + ret = IRQ_HANDLED; + } + + /* Check for dma error (overrun) */ + if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) { + dev_err(player->dev, "DMA error detected"); + + /* Disable interrupt so doesn't continually fire */ + SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); + + /* Stop the player */ + snd_pcm_stream_lock(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(player->substream); + + ret = IRQ_HANDLED; + } + + /* Check for underflow recovery done */ + if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) { + if (!player->info->underflow_enabled) { + dev_err(player->dev, "unexpected Underflow recovering"); + return -EPERM; + } + /* Read the underflow recovery duration */ + tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player); + + /* Clear the underflow recovery duration */ + SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player); + + /* Update state to started */ + player->state = UNIPERIF_STATE_STARTED; + + ret = IRQ_HANDLED; + } + + /* Check if underflow recovery failed */ + if (unlikely(status & + UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) { + dev_err(player->dev, "Underflow recovery failed"); + + /* Stop the player */ + snd_pcm_stream_lock(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(player->substream); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int uni_player_clk_set_rate(struct uniperif *player, unsigned long rate) +{ + int rate_adjusted, rate_achieved, delta, ret; + int adjustment = player->clk_adj; + + /* + * a + * F = f + --------- * f = f + d + * 1000000 + * + * a + * d = --------- * f + * 1000000 + * + * where: + * f - nominal rate + * a - adjustment in ppm (parts per milion) + * F - rate to be set in synthesizer + * d - delta (difference) between f and F + */ + if (adjustment < 0) { + /* div64_64 operates on unsigned values... */ + delta = -1; + adjustment = -adjustment; + } else { + delta = 1; + } + /* 500000 ppm is 0.5, which is used to round up values */ + delta *= (int)div64_u64((uint64_t)rate * + (uint64_t)adjustment + 500000, 1000000); + rate_adjusted = rate + delta; + + /* Adjusted rate should never be == 0 */ + if (!rate_adjusted) + return -EINVAL; + + ret = clk_set_rate(player->clk, rate_adjusted); + if (ret < 0) + return ret; + + rate_achieved = clk_get_rate(player->clk); + if (!rate_achieved) + /* If value is 0 means that clock or parent not valid */ + return -EINVAL; + + /* + * Using ALSA's adjustment control, we can modify the rate to be up + * to twice as much as requested, but no more + */ + delta = rate_achieved - rate; + if (delta < 0) { + /* div64_64 operates on unsigned values... */ + delta = -delta; + adjustment = -1; + } else { + adjustment = 1; + } + /* Frequency/2 is added to round up result */ + adjustment *= (int)div64_u64((uint64_t)delta * 1000000 + rate / 2, + rate); + player->clk_adj = adjustment; + return 0; +} + +static void uni_player_set_channel_status(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + int n; + unsigned int status; + + /* + * Some AVRs and TVs require the channel status to contain a correct + * sampling frequency. If no sample rate is already specified, then + * set one. + */ + mutex_lock(&player->ctrl_lock); + if (runtime && (player->stream_settings.iec958.status[3] + == IEC958_AES3_CON_FS_NOTID)) { + switch (runtime->rate) { + case 22050: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_22050; + break; + case 44100: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_44100; + break; + case 88200: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_88200; + break; + case 176400: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_176400; + break; + case 24000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_24000; + break; + case 48000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_48000; + break; + case 96000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_96000; + break; + case 192000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_192000; + break; + case 32000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_32000; + break; + default: + /* Mark as sampling frequency not indicated */ + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_NOTID; + break; + } + } + + /* Audio mode: + * Use audio mode status to select PCM or encoded mode + */ + if (player->stream_settings.iec958.status[0] & IEC958_AES0_NONAUDIO) + player->stream_settings.encoding_mode = + UNIPERIF_IEC958_ENCODING_MODE_ENCODED; + else + player->stream_settings.encoding_mode = + UNIPERIF_IEC958_ENCODING_MODE_PCM; + + if (player->stream_settings.encoding_mode == + UNIPERIF_IEC958_ENCODING_MODE_PCM) + /* Clear user validity bits */ + SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0); + else + /* Set user validity bits */ + SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 1); + + /* Program the new channel status */ + for (n = 0; n < 6; ++n) { + status = + player->stream_settings.iec958.status[0 + (n * 4)] & 0xf; + status |= + player->stream_settings.iec958.status[1 + (n * 4)] << 8; + status |= + player->stream_settings.iec958.status[2 + (n * 4)] << 16; + status |= + player->stream_settings.iec958.status[3 + (n * 4)] << 24; + SET_UNIPERIF_CHANNEL_STA_REGN(player, n, status); + } + mutex_unlock(&player->ctrl_lock); + + /* Update the channel status */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player); + else + SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player); +} + +static int uni_player_prepare_iec958(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + int clk_div; + + clk_div = player->mclk / runtime->rate; + + /* Oversampling must be multiple of 128 as iec958 frame is 32-bits */ + if ((clk_div % 128) || (clk_div <= 0)) { + dev_err(player->dev, "%s: invalid clk_div %d", + __func__, clk_div); + return -EINVAL; + } + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + /* 16/16 memory format */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player); + /* 16-bits per sub-frame */ + SET_UNIPERIF_I2S_FMT_NBIT_32(player); + /* Set 16-bit sample precision */ + SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); + break; + case SNDRV_PCM_FORMAT_S32_LE: + /* 16/0 memory format */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player); + /* 32-bits per sub-frame */ + SET_UNIPERIF_I2S_FMT_NBIT_32(player); + /* Set 24-bit sample precision */ + SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player); + break; + default: + dev_err(player->dev, "format not supported"); + return -EINVAL; + } + + /* Set parity to be calculated by the hardware */ + SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(player); + + /* Set channel status bits to be inserted by the hardware */ + SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(player); + + /* Set user data bits to be inserted by the hardware */ + SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(player); + + /* Set validity bits to be inserted by the hardware */ + SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(player); + + /* Set full software control to disabled */ + SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(player); + + SET_UNIPERIF_CTRL_ZERO_STUFF_HW(player); + + /* Update the channel status */ + uni_player_set_channel_status(player, runtime); + + /* Clear the user validity user bits */ + SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0); + + /* Disable one-bit audio mode */ + SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player); + + /* Enable consecutive frames repetition of Z preamble (not for HBRA) */ + SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(player); + + /* Change to SUF0_SUBF1 and left/right channels swap! */ + SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(player); + + /* Set data output as MSB first */ + SET_UNIPERIF_I2S_FMT_ORDER_MSB(player); + + if (player->stream_settings.encoding_mode == + UNIPERIF_IEC958_ENCODING_MODE_ENCODED) + SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(player); + else + SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(player); + + SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2); + + /* Set rounding to off */ + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + + /* Set clock divisor */ + SET_UNIPERIF_CTRL_DIVIDER(player, clk_div / 128); + + /* Set the spdif latency to not wait before starting player */ + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player); + + /* + * Ensure iec958 formatting is off. It will be enabled in function + * uni_player_start() at the same time as the operation + * mode is set to work around a silicon issue. + */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player); + else + SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player); + + return 0; +} + +static int uni_player_prepare_pcm(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + int output_frame_size, slot_width, clk_div; + + /* Force slot width to 32 in I2S mode (HW constraint) */ + if ((player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == + SND_SOC_DAIFMT_I2S) { + slot_width = 32; + } else { + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 16; + break; + default: + slot_width = 32; + break; + } + } + output_frame_size = slot_width * runtime->channels; + + clk_div = player->mclk / runtime->rate; + /* + * For 32 bits subframe clk_div must be a multiple of 128, + * for 16 bits must be a multiple of 64 + */ + if ((slot_width == 32) && (clk_div % 128)) { + dev_err(player->dev, "%s: invalid clk_div", __func__); + return -EINVAL; + } + + if ((slot_width == 16) && (clk_div % 64)) { + dev_err(player->dev, "%s: invalid clk_div", __func__); + return -EINVAL; + } + + /* + * Number of bits per subframe (which is one channel sample) + * on output - Transfer 16 or 32 bits from FIFO + */ + switch (slot_width) { + case 32: + SET_UNIPERIF_I2S_FMT_NBIT_32(player); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player); + break; + case 16: + SET_UNIPERIF_I2S_FMT_NBIT_16(player); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); + break; + default: + dev_err(player->dev, "subframe format not supported"); + return -EINVAL; + } + + /* Configure data memory format */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + /* One data word contains two samples */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player); + break; + + case SNDRV_PCM_FORMAT_S32_LE: + /* + * Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits + * on the left than zeros (if less than 32 bytes)"... ;-) + */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player); + break; + + default: + dev_err(player->dev, "format not supported"); + return -EINVAL; + } + + /* Set rounding to off */ + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + + /* Set clock divisor */ + SET_UNIPERIF_CTRL_DIVIDER(player, clk_div / (2 * output_frame_size)); + + /* Number of channelsmust be even*/ + if ((runtime->channels % 2) || (runtime->channels < 2) || + (runtime->channels > 10)) { + dev_err(player->dev, "%s: invalid nb of channels", __func__); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2); + + /* Set 1-bit audio format to disabled */ + SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player); + + SET_UNIPERIF_I2S_FMT_ORDER_MSB(player); + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player); + + /* No iec958 formatting as outputting to DAC */ + SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player); + + return 0; +} + +/* + * ALSA uniperipheral iec958 controls + */ +static int uni_player_ctl_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int uni_player_ctl_iec958_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958; + + mutex_lock(&player->ctrl_lock); + ucontrol->value.iec958.status[0] = iec958->status[0]; + ucontrol->value.iec958.status[1] = iec958->status[1]; + ucontrol->value.iec958.status[2] = iec958->status[2]; + ucontrol->value.iec958.status[3] = iec958->status[3]; + mutex_unlock(&player->ctrl_lock); + return 0; +} + +static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958; + + mutex_lock(&player->ctrl_lock); + iec958->status[0] = ucontrol->value.iec958.status[0]; + iec958->status[1] = ucontrol->value.iec958.status[1]; + iec958->status[2] = ucontrol->value.iec958.status[2]; + iec958->status[3] = ucontrol->value.iec958.status[3]; + mutex_unlock(&player->ctrl_lock); + + uni_player_set_channel_status(player, NULL); + + return 0; +} + +static struct snd_kcontrol_new uni_player_iec958_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = uni_player_ctl_iec958_info, + .get = uni_player_ctl_iec958_get, + .put = uni_player_ctl_iec958_put, +}; + +/* + * uniperif rate adjustement control + */ +static int snd_sti_clk_adjustment_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = UNIPERIF_PLAYER_CLK_ADJ_MIN; + uinfo->value.integer.max = UNIPERIF_PLAYER_CLK_ADJ_MAX; + uinfo->value.integer.step = 1; + + return 0; +} + +static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + + mutex_lock(&player->ctrl_lock); + ucontrol->value.integer.value[0] = player->clk_adj; + mutex_unlock(&player->ctrl_lock); + + return 0; +} + +static int snd_sti_clk_adjustment_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + int ret = 0; + + if ((ucontrol->value.integer.value[0] < UNIPERIF_PLAYER_CLK_ADJ_MIN) || + (ucontrol->value.integer.value[0] > UNIPERIF_PLAYER_CLK_ADJ_MAX)) + return -EINVAL; + + mutex_lock(&player->ctrl_lock); + player->clk_adj = ucontrol->value.integer.value[0]; + + if (player->mclk) + ret = uni_player_clk_set_rate(player, player->mclk); + mutex_unlock(&player->ctrl_lock); + + return ret; +} + +static struct snd_kcontrol_new uni_player_clk_adj_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Playback Oversampling Freq. Adjustment", + .info = snd_sti_clk_adjustment_info, + .get = snd_sti_clk_adjustment_get, + .put = snd_sti_clk_adjustment_put, +}; + +static struct snd_kcontrol_new *snd_sti_pcm_ctl[] = { + &uni_player_clk_adj_ctl, +}; + +static struct snd_kcontrol_new *snd_sti_iec_ctl[] = { + &uni_player_iec958_ctl, + &uni_player_clk_adj_ctl, +}; + +static int uni_player_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + + player->clk_adj = 0; + + return 0; +} + +static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + int ret; + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + if (clk_id != 0) + return -EINVAL; + + mutex_lock(&player->ctrl_lock); + ret = uni_player_clk_set_rate(player, freq); + if (!ret) + player->mclk = freq; + mutex_unlock(&player->ctrl_lock); + + return ret; +} + +static int uni_player_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + struct snd_pcm_runtime *runtime = substream->runtime; + int transfer_size, trigger_limit; + int ret; + + /* The player should be stopped */ + if (player->state != UNIPERIF_STATE_STOPPED) { + dev_err(player->dev, "%s: invalid player state %d", __func__, + player->state); + return -EINVAL; + } + + /* Calculate transfer size (in fifo cells and bytes) for frame count */ + transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES; + + /* Calculate number of empty cells available before asserting DREQ */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { + trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size; + } else { + /* + * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 + * FDMA_TRIGGER_LIMIT also controls when the state switches + * from OFF or STANDBY to AUDIO DATA. + */ + trigger_limit = transfer_size; + } + + /* Trigger limit must be an even number */ + if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) || + (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) { + dev_err(player->dev, "invalid trigger limit %d", trigger_limit); + return -EINVAL; + } + + SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit); + + /* Uniperipheral setup depends on player type */ + switch (player->info->player_type) { + case SND_ST_UNIPERIF_PLAYER_TYPE_HDMI: + ret = uni_player_prepare_iec958(player, runtime); + break; + case SND_ST_UNIPERIF_PLAYER_TYPE_PCM: + ret = uni_player_prepare_pcm(player, runtime); + break; + case SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF: + ret = uni_player_prepare_iec958(player, runtime); + break; + default: + dev_err(player->dev, "invalid player type"); + return -EINVAL; + } + + if (ret) + return ret; + + switch (player->daifmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player); + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player); + break; + case SND_SOC_DAIFMT_NB_IF: + SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player); + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player); + break; + case SND_SOC_DAIFMT_IB_NF: + SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player); + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player); + break; + case SND_SOC_DAIFMT_IB_IF: + SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player); + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player); + break; + } + + switch (player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player); + SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(player); + break; + case SND_SOC_DAIFMT_LEFT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); + break; + case SND_SOC_DAIFMT_RIGHT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(player); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); + break; + default: + dev_err(player->dev, "format not supported"); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0); + + /* Reset uniperipheral player */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(player); + + return reset_player(player); +} + +static int uni_player_start(struct uniperif *player) +{ + int ret; + + /* The player should be stopped */ + if (player->state != UNIPERIF_STATE_STOPPED) { + dev_err(player->dev, "%s: invalid player state", __func__); + return -EINVAL; + } + + ret = clk_prepare_enable(player->clk); + if (ret) { + dev_err(player->dev, "%s: Failed to enable clock", __func__); + return ret; + } + + /* Clear any pending interrupts */ + SET_UNIPERIF_ITS_BCLR(player, GET_UNIPERIF_ITS(player)); + + /* Set the interrupt mask */ + SET_UNIPERIF_ITM_BSET_DMA_ERROR(player); + SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player); + + /* Enable underflow recovery interrupts */ + if (player->info->underflow_enabled) { + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player); + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player); + } + + /* Reset uniperipheral player */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(player); + + ret = reset_player(player); + if (ret < 0) + return ret; + + /* + * Does not use IEC61937 features of the uniperipheral hardware. + * Instead it performs IEC61937 in software and inserts it directly + * into the audio data stream. As such, when encoded mode is selected, + * linear pcm mode is still used, but with the differences of the + * channel status bits set for encoded mode and the validity bits set. + */ + SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(player); + + /* + * If iec958 formatting is required for hdmi or spdif, then it must be + * enabled after the operation mode is set. If set prior to this, it + * will not take affect and hang the player. + */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) + SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player); + + /* Force channel status update (no update if clk disable) */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player); + else + SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player); + + /* Update state to started */ + player->state = UNIPERIF_STATE_STARTED; + + return 0; +} + +static int uni_player_stop(struct uniperif *player) +{ + int ret; + + /* The player should not be in stopped state */ + if (player->state == UNIPERIF_STATE_STOPPED) { + dev_err(player->dev, "%s: invalid player state", __func__); + return -EINVAL; + } + + /* Turn the player off */ + SET_UNIPERIF_CTRL_OPERATION_OFF(player); + + /* Soft reset the player */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(player); + + ret = reset_player(player); + if (ret < 0) + return ret; + + /* Disable interrupts */ + SET_UNIPERIF_ITM_BCLR(player, GET_UNIPERIF_ITM(player)); + + /* Disable clock */ + clk_disable_unprepare(player->clk); + + /* Update state to stopped and return */ + player->state = UNIPERIF_STATE_STOPPED; + + return 0; +} + +int uni_player_resume(struct uniperif *player) +{ + int ret; + + /* Select the frequency synthesizer clock */ + if (player->clk_sel) { + ret = regmap_field_write(player->clk_sel, 1); + if (ret) { + dev_err(player->dev, + "%s: Failed to select freq synth clock", + __func__); + return ret; + } + } + + SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player); + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player); + SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player); + + return 0; +} +EXPORT_SYMBOL_GPL(uni_player_resume); + +static int uni_player_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + return uni_player_start(player); + case SNDRV_PCM_TRIGGER_STOP: + return uni_player_stop(player); + case SNDRV_PCM_TRIGGER_RESUME: + return uni_player_resume(player); + default: + return -EINVAL; + } +} + +static void uni_player_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *player = priv->dai_data.uni; + + if (player->state != UNIPERIF_STATE_STOPPED) + /* Stop the player */ + uni_player_stop(player); +} + +static int uni_player_parse_dt_clk_glue(struct platform_device *pdev, + struct uniperif *player) +{ + int bit_offset; + struct device_node *node = pdev->dev.of_node; + struct regmap *regmap; + + bit_offset = SYS_CFG_AUDI0_GLUE_PCM_CLKX + player->info->id; + + regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg"); + + if (regmap) { + struct reg_field regfield = + REG_FIELD(SYS_CFG_AUDIO_GLUE, bit_offset, bit_offset); + + player->clk_sel = regmap_field_alloc(regmap, regfield); + } else { + dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n"); + return -EINVAL; + } + + return 0; +} + +static int uni_player_parse_dt(struct platform_device *pdev, + struct uniperif *player) +{ + struct uniperif_info *info; + struct device *dev = &pdev->dev; + struct device_node *pnode = pdev->dev.of_node; + const char *mode; + + /* Allocate memory for the info structure */ + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + of_property_read_u32(pnode, "version", &player->ver); + if (player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { + dev_err(dev, "Unknown uniperipheral version "); + return -EINVAL; + } + /* Underflow recovery is only supported on later ip revisions */ + if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + info->underflow_enabled = 1; + + of_property_read_u32(pnode, "uniperiph-id", &info->id); + + /* Read the device mode property */ + of_property_read_string(pnode, "mode", &mode); + + if (strcasecmp(mode, "hdmi") == 0) + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI; + else if (strcasecmp(mode, "pcm") == 0) + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_PCM; + else if (strcasecmp(mode, "spdif") == 0) + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF; + else + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_NONE; + + /* Save the info structure */ + player->info = info; + + /* Get the PCM_CLK_SEL bit from audio-glue-ctrl SoC register */ + if (uni_player_parse_dt_clk_glue(pdev, player)) + return -EINVAL; + + return 0; +} + +static const struct snd_soc_dai_ops uni_player_dai_ops = { + .startup = uni_player_startup, + .shutdown = uni_player_shutdown, + .prepare = uni_player_prepare, + .trigger = uni_player_trigger, + .hw_params = sti_uniperiph_dai_hw_params, + .set_fmt = sti_uniperiph_dai_set_fmt, + .set_sysclk = uni_player_set_sysclk +}; + +int uni_player_init(struct platform_device *pdev, + struct uniperif *player) +{ + int ret = 0; + + player->dev = &pdev->dev; + player->state = UNIPERIF_STATE_STOPPED; + player->hw = &uni_player_pcm_hw; + player->dai_ops = &uni_player_dai_ops; + + ret = uni_player_parse_dt(pdev, player); + + if (ret < 0) { + dev_err(player->dev, "Failed to parse DeviceTree"); + return ret; + } + + /* Get uniperif resource */ + player->clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(player->clk)) + ret = PTR_ERR(player->clk); + + /* Select the frequency synthesizer clock */ + if (player->clk_sel) { + ret = regmap_field_write(player->clk_sel, 1); + if (ret) { + dev_err(player->dev, + "%s: Failed to select freq synth clock", + __func__); + return ret; + } + } + + ret = devm_request_irq(&pdev->dev, player->irq, + uni_player_irq_handler, IRQF_SHARED, + dev_name(&pdev->dev), player); + if (ret < 0) + return ret; + + mutex_init(&player->ctrl_lock); + + /* Ensure that disabled by default */ + SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player); + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player); + SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player); + + if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) { + /* Set default iec958 status bits */ + + /* Consumer, PCM, copyright, 2ch, mode 0 */ + player->stream_settings.iec958.status[0] = 0x00; + /* Broadcast reception category */ + player->stream_settings.iec958.status[1] = + IEC958_AES1_CON_GENERAL; + /* Do not take into account source or channel number */ + player->stream_settings.iec958.status[2] = + IEC958_AES2_CON_SOURCE_UNSPEC; + /* Sampling frequency not indicated */ + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_NOTID; + /* Max sample word 24-bit, sample word length not indicated */ + player->stream_settings.iec958.status[4] = + IEC958_AES4_CON_MAX_WORDLEN_24 | + IEC958_AES4_CON_WORDLEN_24_20; + + player->num_ctrls = ARRAY_SIZE(snd_sti_iec_ctl); + player->snd_ctrls = snd_sti_iec_ctl[0]; + } else { + player->num_ctrls = ARRAY_SIZE(snd_sti_pcm_ctl); + player->snd_ctrls = snd_sti_pcm_ctl[0]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(uni_player_init); diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c new file mode 100644 index 0000000..c502626 --- /dev/null +++ b/sound/soc/sti/uniperif_reader.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <sound/soc.h> + +#include "uniperif.h" + +/* + * Note: snd_pcm_hardware is linked to DMA controller but is declared here to + * integrate unireader capability in term of rate and supported channels + */ +static const struct snd_pcm_hardware uni_reader_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 96000, + + .channels_min = 2, + .channels_max = 8, + + .periods_min = 2, + .periods_max = 48, + + .period_bytes_min = 128, + .period_bytes_max = 64 * PAGE_SIZE, + .buffer_bytes_max = 256 * PAGE_SIZE +}; + +/* + * uni_reader_irq_handler + * In case of error audio stream is stopped; stop action is protected via PCM + * stream lock to avoid race condition with trigger callback. + */ +static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + struct uniperif *reader = dev_id; + unsigned int status; + + if (reader->state == UNIPERIF_STATE_STOPPED) { + /* Unexpected IRQ: do nothing */ + dev_warn(reader->dev, "unexpected IRQ "); + return IRQ_HANDLED; + } + + /* Get interrupt status & clear them immediately */ + status = GET_UNIPERIF_ITS(reader); + SET_UNIPERIF_ITS_BCLR(reader, status); + + /* Check for fifo overflow error */ + if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { + dev_err(reader->dev, "FIFO error detected"); + + snd_pcm_stream_lock(reader->substream); + snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(reader->substream); + + return IRQ_HANDLED; + } + + return ret; +} + +static int uni_reader_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *reader = priv->dai_data.uni; + struct snd_pcm_runtime *runtime = substream->runtime; + int transfer_size, trigger_limit; + int slot_width; + int count = 10; + + /* The reader should be stopped */ + if (reader->state != UNIPERIF_STATE_STOPPED) { + dev_err(reader->dev, "%s: invalid reader state %d", __func__, + reader->state); + return -EINVAL; + } + + /* Calculate transfer size (in fifo cells and bytes) for frame count */ + transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES; + + /* Calculate number of empty cells available before asserting DREQ */ + if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size; + else + /* + * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 + * FDMA_TRIGGER_LIMIT also controls when the state switches + * from OFF or STANDBY to AUDIO DATA. + */ + trigger_limit = transfer_size; + + /* Trigger limit must be an even number */ + if ((!trigger_limit % 2) || + (trigger_limit != 1 && transfer_size % 2) || + (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) { + dev_err(reader->dev, "invalid trigger limit %d", trigger_limit); + return -EINVAL; + } + + SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit); + + switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_NB_IF: + SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader); + break; + default: + SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader); + } + + /* Force slot width to 32 in I2S mode */ + if ((reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) + == SND_SOC_DAIFMT_I2S) { + slot_width = 32; + } else { + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 16; + break; + default: + slot_width = 32; + break; + } + } + + /* Number of bits per subframe (i.e one channel sample) on input. */ + switch (slot_width) { + case 32: + SET_UNIPERIF_I2S_FMT_NBIT_32(reader); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(reader); + break; + case 16: + SET_UNIPERIF_I2S_FMT_NBIT_16(reader); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(reader); + break; + default: + dev_err(reader->dev, "subframe format not supported"); + return -EINVAL; + } + + /* Configure data memory format */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + /* One data word contains two samples */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_16(reader); + break; + + case SNDRV_PCM_FORMAT_S32_LE: + /* + * Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits + * on the MSB then zeros (if less than 32 bytes)"... + */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_0(reader); + break; + + default: + dev_err(reader->dev, "format not supported"); + return -EINVAL; + } + + switch (reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader); + SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(reader); + break; + case SND_SOC_DAIFMT_LEFT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); + break; + case SND_SOC_DAIFMT_RIGHT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(reader); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); + break; + default: + dev_err(reader->dev, "format not supported"); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader); + + /* Data clocking (changing) on the rising edge */ + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader); + + /* Number of channels must be even */ + + if ((runtime->channels % 2) || (runtime->channels < 2) || + (runtime->channels > 10)) { + dev_err(reader->dev, "%s: invalid nb of channels", __func__); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2); + + /* Clear any pending interrupts */ + SET_UNIPERIF_ITS_BCLR(reader, GET_UNIPERIF_ITS(reader)); + + SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(reader, 0); + + /* Set the interrupt mask */ + SET_UNIPERIF_ITM_BSET_DMA_ERROR(reader); + SET_UNIPERIF_ITM_BSET_FIFO_ERROR(reader); + SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(reader); + + /* Enable underflow recovery interrupts */ + if (reader->info->underflow_enabled) { + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(reader); + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(reader); + } + + /* Reset uniperipheral reader */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(reader); + + while (GET_UNIPERIF_SOFT_RST_SOFT_RST(reader)) { + udelay(5); + count--; + } + if (!count) { + dev_err(reader->dev, "Failed to reset uniperif"); + return -EIO; + } + + return 0; +} + +static int uni_reader_start(struct uniperif *reader) +{ + /* The reader should be stopped */ + if (reader->state != UNIPERIF_STATE_STOPPED) { + dev_err(reader->dev, "%s: invalid reader state", __func__); + return -EINVAL; + } + + /* Enable reader interrupts (and clear possible stalled ones) */ + SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(reader); + SET_UNIPERIF_ITM_BSET_FIFO_ERROR(reader); + + /* Launch the reader */ + SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(reader); + + /* Update state to started */ + reader->state = UNIPERIF_STATE_STARTED; + return 0; +} + +static int uni_reader_stop(struct uniperif *reader) +{ + /* The reader should not be in stopped state */ + if (reader->state == UNIPERIF_STATE_STOPPED) { + dev_err(reader->dev, "%s: invalid reader state", __func__); + return -EINVAL; + } + + /* Turn the reader off */ + SET_UNIPERIF_CTRL_OPERATION_OFF(reader); + + /* Disable interrupts */ + SET_UNIPERIF_ITM_BCLR(reader, GET_UNIPERIF_ITM(reader)); + + /* Update state to stopped and return */ + reader->state = UNIPERIF_STATE_STOPPED; + + return 0; +} + +static int uni_reader_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *reader = priv->dai_data.uni; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + return uni_reader_start(reader); + case SNDRV_PCM_TRIGGER_STOP: + return uni_reader_stop(reader); + default: + return -EINVAL; + } +} + +static void uni_reader_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct uniperif *reader = priv->dai_data.uni; + + if (reader->state != UNIPERIF_STATE_STOPPED) { + /* Stop the reader */ + uni_reader_stop(reader); + } +} + +static int uni_reader_parse_dt(struct platform_device *pdev, + struct uniperif *reader) +{ + struct uniperif_info *info; + struct device_node *node = pdev->dev.of_node; + + /* Allocate memory for the info structure */ + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + of_property_read_u32(node, "version", &reader->ver); + + /* Save the info structure */ + reader->info = info; + + return 0; +} + +static const struct snd_soc_dai_ops uni_reader_dai_ops = { + .shutdown = uni_reader_shutdown, + .prepare = uni_reader_prepare, + .trigger = uni_reader_trigger, + .hw_params = sti_uniperiph_dai_hw_params, + .set_fmt = sti_uniperiph_dai_set_fmt, +}; + +int uni_reader_init(struct platform_device *pdev, + struct uniperif *reader) +{ + int ret = 0; + + reader->dev = &pdev->dev; + reader->state = UNIPERIF_STATE_STOPPED; + reader->hw = &uni_reader_pcm_hw; + reader->dai_ops = &uni_reader_dai_ops; + + dev_err(reader->dev, "%s: enter\n", __func__); + ret = uni_reader_parse_dt(pdev, reader); + if (ret < 0) { + dev_err(reader->dev, "Failed to parse DeviceTree"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, reader->irq, + uni_reader_irq_handler, IRQF_SHARED, + dev_name(&pdev->dev), reader); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request IRQ"); + return -EBUSY; + } + + return 0; +} +EXPORT_SYMBOL_GPL(uni_reader_init); diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c index f52600b..89add13 100644 --- a/sound/soc/tegra/tegra20_das.c +++ b/sound/soc/tegra/tegra20_das.c @@ -133,7 +133,7 @@ static const struct regmap_config tegra20_das_regmap_config = { static int tegra20_das_probe(struct platform_device *pdev) { - struct resource *res, *region; + struct resource *res; void __iomem *regs; int ret = 0; @@ -149,24 +149,9 @@ static int tegra20_das_probe(struct platform_device *pdev) das->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err; - } - - region = devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name); - if (!region) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err; - } - - regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); goto err; } diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 05f1c6e..14106fa 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -339,7 +339,7 @@ static const struct regmap_config tegra20_i2s_regmap_config = { static int tegra20_i2s_platform_probe(struct platform_device *pdev) { struct tegra20_i2s *i2s; - struct resource *mem, *memregion; + struct resource *mem; void __iomem *regs; int ret; @@ -362,24 +362,9 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev) } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - - memregion = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), DRV_NAME); - if (!memregion) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err_clk_put; - } - - regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; + regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); goto err_clk_put; } diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 9141477..a0c3640 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -265,7 +265,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = { static int tegra20_spdif_platform_probe(struct platform_device *pdev) { struct tegra20_spdif *spdif; - struct resource *mem, *memregion, *dmareq; + struct resource *mem, *dmareq; void __iomem *regs; int ret; @@ -273,45 +273,26 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) GFP_KERNEL); if (!spdif) { dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } dev_set_drvdata(&pdev->dev, spdif); - spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out"); + spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); if (IS_ERR(spdif->clk_spdif_out)) { pr_err("Can't retrieve spdif clock\n"); ret = PTR_ERR(spdif->clk_spdif_out); - goto err; + return ret; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } + regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmareq) { dev_err(&pdev->dev, "No DMA resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - - memregion = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), DRV_NAME); - if (!memregion) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err_clk_put; - } - - regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err_clk_put; + return -ENODEV; } spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, @@ -319,7 +300,7 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) if (IS_ERR(spdif->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); ret = PTR_ERR(spdif->regmap); - goto err_clk_put; + return ret; } spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; @@ -335,7 +316,7 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) } ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, - &tegra20_spdif_dai, 1); + &tegra20_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ret = -ENOMEM; @@ -357,16 +338,12 @@ err_suspend: tegra20_spdif_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_clk_put: - clk_put(spdif->clk_spdif_out); -err: + return ret; } static int tegra20_spdif_platform_remove(struct platform_device *pdev) { - struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) tegra20_spdif_runtime_suspend(&pdev->dev); @@ -374,8 +351,6 @@ static int tegra20_spdif_platform_remove(struct platform_device *pdev) tegra_pcm_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - clk_put(spdif->clk_spdif_out); - return 0; } diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index bc94e5d..fef3b9a 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -521,7 +521,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev) const struct tegra30_ahub_soc_data *soc_data; struct reset_control *rst; int i; - struct resource *res0, *res1, *region; + struct resource *res0, *res1; void __iomem *regs_apbif, *regs_ahub; int ret = 0; @@ -549,103 +549,67 @@ static int tegra30_ahub_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't get reset %s\n", configlink_mods[i].rst_name); ret = PTR_ERR(rst); - goto err; + return ret; } ret = reset_control_deassert(rst); reset_control_put(rst); if (ret) - goto err; + return ret; } ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), GFP_KERNEL); if (!ahub) { dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } dev_set_drvdata(&pdev->dev, ahub); ahub->soc_data = soc_data; ahub->dev = &pdev->dev; - ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio"); + ahub->clk_d_audio = devm_clk_get(&pdev->dev, "d_audio"); if (IS_ERR(ahub->clk_d_audio)) { dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n"); ret = PTR_ERR(ahub->clk_d_audio); - goto err; + return ret; } - ahub->clk_apbif = clk_get(&pdev->dev, "apbif"); + ahub->clk_apbif = devm_clk_get(&pdev->dev, "apbif"); if (IS_ERR(ahub->clk_apbif)) { dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n"); ret = PTR_ERR(ahub->clk_apbif); - goto err_clk_put_d_audio; + return ret; } res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res0) { - dev_err(&pdev->dev, "No apbif memory resource\n"); - ret = -ENODEV; - goto err_clk_put_apbif; - } + regs_apbif = devm_ioremap_resource(&pdev->dev, res0); + if (IS_ERR(regs_apbif)) + return PTR_ERR(regs_apbif); - region = devm_request_mem_region(&pdev->dev, res0->start, - resource_size(res0), DRV_NAME); - if (!region) { - dev_err(&pdev->dev, "request region apbif failed\n"); - ret = -EBUSY; - goto err_clk_put_apbif; - } ahub->apbif_addr = res0->start; - regs_apbif = devm_ioremap(&pdev->dev, res0->start, - resource_size(res0)); - if (!regs_apbif) { - dev_err(&pdev->dev, "ioremap apbif failed\n"); - ret = -ENOMEM; - goto err_clk_put_apbif; - } - ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, &tegra30_ahub_apbif_regmap_config); if (IS_ERR(ahub->regmap_apbif)) { dev_err(&pdev->dev, "apbif regmap init failed\n"); ret = PTR_ERR(ahub->regmap_apbif); - goto err_clk_put_apbif; + return ret; } regcache_cache_only(ahub->regmap_apbif, true); res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res1) { - dev_err(&pdev->dev, "No ahub memory resource\n"); - ret = -ENODEV; - goto err_clk_put_apbif; - } - - region = devm_request_mem_region(&pdev->dev, res1->start, - resource_size(res1), DRV_NAME); - if (!region) { - dev_err(&pdev->dev, "request region ahub failed\n"); - ret = -EBUSY; - goto err_clk_put_apbif; - } - - regs_ahub = devm_ioremap(&pdev->dev, res1->start, - resource_size(res1)); - if (!regs_ahub) { - dev_err(&pdev->dev, "ioremap ahub failed\n"); - ret = -ENOMEM; - goto err_clk_put_apbif; - } + regs_ahub = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(regs_ahub)) + return PTR_ERR(regs_ahub); ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, &tegra30_ahub_ahub_regmap_config); if (IS_ERR(ahub->regmap_ahub)) { dev_err(&pdev->dev, "ahub regmap init failed\n"); ret = PTR_ERR(ahub->regmap_ahub); - goto err_clk_put_apbif; + return ret; } regcache_cache_only(ahub->regmap_ahub, true); @@ -662,12 +626,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); -err_clk_put_apbif: - clk_put(ahub->clk_apbif); -err_clk_put_d_audio: - clk_put(ahub->clk_d_audio); - ahub = NULL; -err: + return ret; } @@ -680,11 +639,6 @@ static int tegra30_ahub_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) tegra30_ahub_runtime_suspend(&pdev->dev); - clk_put(ahub->clk_apbif); - clk_put(ahub->clk_d_audio); - - ahub = NULL; - return 0; } diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index fe36375..8e55583 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -379,7 +379,7 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev) struct tegra30_i2s *i2s; const struct of_device_id *match; u32 cif_ids[2]; - struct resource *mem, *memregion; + struct resource *mem; void __iomem *regs; int ret; @@ -419,24 +419,9 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev) } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - - memregion = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), DRV_NAME); - if (!memregion) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - ret = -EBUSY; - goto err_clk_put; - } - - regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; + regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); goto err_clk_put; } diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 88eacfd..a8f705b 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -411,13 +411,8 @@ static struct snd_soc_platform_driver txx9aclc_soc_platform = { static int txx9aclc_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &txx9aclc_soc_platform); -} - -static int txx9aclc_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; + return devm_snd_soc_register_platform(&pdev->dev, + &txx9aclc_soc_platform); } static struct platform_driver txx9aclc_pcm_driver = { @@ -426,7 +421,6 @@ static struct platform_driver txx9aclc_pcm_driver = { }, .probe = txx9aclc_soc_platform_probe, - .remove = txx9aclc_soc_platform_remove, }; module_platform_driver(txx9aclc_pcm_driver); diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 978f2d7..f5df08d 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -773,20 +773,22 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) } prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); - drvdata->pclk = clk_get(&pdev->dev, "apb_pclk"); + drvdata->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); if (IS_ERR(drvdata->pclk)) { ret = (int)PTR_ERR(drvdata->pclk); - dev_err(&pdev->dev, "%s: ERROR: clk_get of pclk failed (%d)!\n", + dev_err(&pdev->dev, + "%s: ERROR: devm_clk_get of pclk failed (%d)!\n", __func__, ret); - goto err_pclk; + return ret; } - drvdata->clk = clk_get(&pdev->dev, NULL); + drvdata->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(drvdata->clk)) { ret = (int)PTR_ERR(drvdata->clk); - dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n", + dev_err(&pdev->dev, + "%s: ERROR: devm_clk_get failed (%d)!\n", __func__, ret); - goto err_clk; + return ret; } ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp, @@ -795,7 +797,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s: ERROR: Failed to init MSP-struct (%d)!", __func__, ret); - goto err_init_msp; + return ret; } dev_set_drvdata(&pdev->dev, drvdata); @@ -804,7 +806,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n", __func__, drvdata->msp->id); - goto err_init_msp; + return ret; } ret = ux500_pcm_register_platform(pdev); @@ -819,13 +821,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) err_reg_plat: snd_soc_unregister_component(&pdev->dev); -err_init_msp: - clk_put(drvdata->clk); -err_clk: - clk_put(drvdata->pclk); -err_pclk: - devm_regulator_put(drvdata->reg_vape); - return ret; } @@ -837,12 +832,8 @@ static int ux500_msp_drv_remove(struct platform_device *pdev) snd_soc_unregister_component(&pdev->dev); - devm_regulator_put(drvdata->reg_vape); prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); - clk_put(drvdata->clk); - clk_put(drvdata->pclk); - ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp); return 0; diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index 1cfb19e..8382ffa 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -75,7 +75,7 @@ struct xtfpga_i2s { * stream in the pcm_close callback it synchronizes with the interrupt * handler by means of synchronize_rcu call. */ - struct snd_pcm_substream *tx_substream; + struct snd_pcm_substream __rcu *tx_substream; unsigned (*tx_fn)(struct xtfpga_i2s *i2s, struct snd_pcm_runtime *runtime, unsigned tx_ptr); @@ -474,11 +474,6 @@ static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd) card->dev, size, size); } -static void xtfpga_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static const struct snd_pcm_ops xtfpga_pcm_ops = { .open = xtfpga_pcm_open, .close = xtfpga_pcm_close, @@ -490,7 +485,6 @@ static const struct snd_pcm_ops xtfpga_pcm_ops = { static const struct snd_soc_platform_driver xtfpga_soc_platform = { .pcm_new = xtfpga_pcm_new, - .pcm_free = xtfpga_pcm_free, .ops = &xtfpga_pcm_ops, }; diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx296702-i2s.c index 1930c42..1cad93d 100644 --- a/sound/soc/zte/zx296702-i2s.c +++ b/sound/soc/zte/zx296702-i2s.c @@ -380,7 +380,7 @@ static int zx_i2s_probe(struct platform_device *pdev) struct zx_i2s_info *zx_i2s; int ret; - zx_i2s = kzalloc(sizeof(*zx_i2s), GFP_KERNEL); + zx_i2s = devm_kzalloc(&pdev->dev, sizeof(*zx_i2s), GFP_KERNEL); if (!zx_i2s) return -ENOMEM; @@ -401,8 +401,8 @@ static int zx_i2s_probe(struct platform_device *pdev) writel_relaxed(0, zx_i2s->reg_base + ZX_I2S_FIFO_CTRL); platform_set_drvdata(pdev, zx_i2s); - ret = snd_soc_register_component(&pdev->dev, &zx_i2s_component, - &zx_i2s_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &zx_i2s_component, + &zx_i2s_dai, 1); if (ret) { dev_err(&pdev->dev, "Register DAI failed: %d\n", ret); return ret; diff --git a/sound/usb/card.c b/sound/usb/card.c index 0450593..18f5664 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -365,13 +365,15 @@ static int snd_usb_audio_create(struct usb_interface *intf, } mutex_init(&chip->mutex); - init_rwsem(&chip->shutdown_rwsem); + init_waitqueue_head(&chip->shutdown_wait); chip->index = idx; chip->dev = dev; chip->card = card; chip->setup = device_setup[idx]; chip->autoclock = autoclock; - chip->probing = 1; + atomic_set(&chip->active, 1); /* avoid autopm during probing */ + atomic_set(&chip->usage_count, 0); + atomic_set(&chip->shutdown, 0); chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -495,13 +497,13 @@ static int usb_audio_probe(struct usb_interface *intf, mutex_lock(®ister_mutex); for (i = 0; i < SNDRV_CARDS; i++) { if (usb_chip[i] && usb_chip[i]->dev == dev) { - if (usb_chip[i]->shutdown) { + if (atomic_read(&usb_chip[i]->shutdown)) { dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); err = -EIO; goto __error; } chip = usb_chip[i]; - chip->probing = 1; + atomic_inc(&chip->active); /* avoid autopm */ break; } } @@ -561,8 +563,8 @@ static int usb_audio_probe(struct usb_interface *intf, usb_chip[chip->index] = chip; chip->num_interfaces++; - chip->probing = 0; usb_set_intfdata(intf, chip); + atomic_dec(&chip->active); mutex_unlock(®ister_mutex); return 0; @@ -570,7 +572,7 @@ static int usb_audio_probe(struct usb_interface *intf, if (chip) { if (!chip->num_interfaces) snd_card_free(chip->card); - chip->probing = 0; + atomic_dec(&chip->active); } mutex_unlock(®ister_mutex); return err; @@ -585,23 +587,23 @@ static void usb_audio_disconnect(struct usb_interface *intf) struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_card *card; struct list_head *p; - bool was_shutdown; if (chip == (void *)-1L) return; card = chip->card; - down_write(&chip->shutdown_rwsem); - was_shutdown = chip->shutdown; - chip->shutdown = 1; - up_write(&chip->shutdown_rwsem); mutex_lock(®ister_mutex); - if (!was_shutdown) { + if (atomic_inc_return(&chip->shutdown) == 1) { struct snd_usb_stream *as; struct snd_usb_endpoint *ep; struct usb_mixer_interface *mixer; + /* wait until all pending tasks done; + * they are protected by snd_usb_lock_shutdown() + */ + wait_event(chip->shutdown_wait, + !atomic_read(&chip->usage_count)); snd_card_disconnect(card); /* release the pcm resources */ list_for_each_entry(as, &chip->pcm_list, list) { @@ -631,28 +633,50 @@ static void usb_audio_disconnect(struct usb_interface *intf) } } -#ifdef CONFIG_PM - -int snd_usb_autoresume(struct snd_usb_audio *chip) +/* lock the shutdown (disconnect) task and autoresume */ +int snd_usb_lock_shutdown(struct snd_usb_audio *chip) { - int err = -ENODEV; + int err; - down_read(&chip->shutdown_rwsem); - if (chip->probing || chip->in_pm) - err = 0; - else if (!chip->shutdown) - err = usb_autopm_get_interface(chip->pm_intf); - up_read(&chip->shutdown_rwsem); + atomic_inc(&chip->usage_count); + if (atomic_read(&chip->shutdown)) { + err = -EIO; + goto error; + } + err = snd_usb_autoresume(chip); + if (err < 0) + goto error; + return 0; + error: + if (atomic_dec_and_test(&chip->usage_count)) + wake_up(&chip->shutdown_wait); return err; } +/* autosuspend and unlock the shutdown */ +void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) +{ + snd_usb_autosuspend(chip); + if (atomic_dec_and_test(&chip->usage_count)) + wake_up(&chip->shutdown_wait); +} + +#ifdef CONFIG_PM + +int snd_usb_autoresume(struct snd_usb_audio *chip) +{ + if (atomic_read(&chip->shutdown)) + return -EIO; + if (atomic_inc_return(&chip->active) == 1) + return usb_autopm_get_interface(chip->pm_intf); + return 0; +} + void snd_usb_autosuspend(struct snd_usb_audio *chip) { - down_read(&chip->shutdown_rwsem); - if (!chip->shutdown && !chip->probing && !chip->in_pm) + if (atomic_dec_and_test(&chip->active)) usb_autopm_put_interface(chip->pm_intf); - up_read(&chip->shutdown_rwsem); } static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) @@ -665,30 +689,20 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) if (chip == (void *)-1L) return 0; - if (!PMSG_IS_AUTO(message)) { + chip->autosuspended = !!PMSG_IS_AUTO(message); + if (!chip->autosuspended) snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); - if (!chip->num_suspended_intf++) { - list_for_each_entry(as, &chip->pcm_list, list) { - snd_pcm_suspend_all(as->pcm); - as->substream[0].need_setup_ep = - as->substream[1].need_setup_ep = true; - } - list_for_each(p, &chip->midi_list) { - snd_usbmidi_suspend(p); - } + if (!chip->num_suspended_intf++) { + list_for_each_entry(as, &chip->pcm_list, list) { + snd_pcm_suspend_all(as->pcm); + as->substream[0].need_setup_ep = + as->substream[1].need_setup_ep = true; } - } else { - /* - * otherwise we keep the rest of the system in the dark - * to keep this transparent - */ - if (!chip->num_suspended_intf++) - chip->autosuspended = 1; - } - - if (chip->num_suspended_intf == 1) + list_for_each(p, &chip->midi_list) + snd_usbmidi_suspend(p); list_for_each_entry(mixer, &chip->mixer_list, list) snd_usb_mixer_suspend(mixer); + } return 0; } @@ -705,7 +719,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) if (--chip->num_suspended_intf) return 0; - chip->in_pm = 1; + atomic_inc(&chip->active); /* avoid autopm */ /* * ALSA leaves material resumption to user space * we just notify and restart the mixers @@ -725,7 +739,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) chip->autosuspended = 0; err_out: - chip->in_pm = 0; + atomic_dec(&chip->active); /* allow autopm after this point */ return err; } diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 03b0744..e6f7189 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -355,8 +355,10 @@ static void snd_complete_urb(struct urb *urb) if (unlikely(urb->status == -ENOENT || /* unlinked */ urb->status == -ENODEV || /* device removed */ urb->status == -ECONNRESET || /* unlinked */ - urb->status == -ESHUTDOWN || /* device disabled */ - ep->chip->shutdown)) /* device disconnected */ + urb->status == -ESHUTDOWN)) /* device disabled */ + goto exit_clear; + /* device disconnected */ + if (unlikely(atomic_read(&ep->chip->shutdown))) goto exit_clear; if (usb_pipeout(ep->pipe)) { @@ -529,7 +531,7 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force) { unsigned int i; - if (!force && ep->chip->shutdown) /* to be sure... */ + if (!force && atomic_read(&ep->chip->shutdown)) /* to be sure... */ return -EBADFD; clear_bit(EP_FLAG_RUNNING, &ep->flags); @@ -868,7 +870,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep) int err; unsigned int i; - if (ep->chip->shutdown) + if (atomic_read(&ep->chip->shutdown)) return -EBADFD; /* already running? */ diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 6b3acba..f494dce 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -282,6 +282,21 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val) return val; } +static int uac2_ctl_value_size(int val_type) +{ + switch (val_type) { + case USB_MIXER_S32: + case USB_MIXER_U32: + return 4; + case USB_MIXER_S16: + case USB_MIXER_U16: + return 2; + default: + return 1; + } + return 0; /* unreachable */ +} + /* * retrieve a mixer value @@ -296,14 +311,11 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int timeout = 10; int idx = 0, err; - err = snd_usb_autoresume(chip); + err = snd_usb_lock_shutdown(chip); if (err < 0) return -EIO; - down_read(&chip->shutdown_rwsem); while (timeout-- > 0) { - if (chip->shutdown) - break; idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, @@ -319,8 +331,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, err = -EINVAL; out: - up_read(&chip->shutdown_rwsem); - snd_usb_autosuspend(chip); + snd_usb_unlock_shutdown(chip); return err; } @@ -328,14 +339,14 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { struct snd_usb_audio *chip = cval->head.mixer->chip; - unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */ + unsigned char buf[4 + 3 * sizeof(__u32)]; /* enough space for one range */ unsigned char *val; int idx = 0, ret, size; __u8 bRequest; if (request == UAC_GET_CUR) { bRequest = UAC2_CS_CUR; - size = sizeof(__u16); + size = uac2_ctl_value_size(cval->val_type); } else { bRequest = UAC2_CS_RANGE; size = sizeof(buf); @@ -343,21 +354,15 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, memset(buf, 0, sizeof(buf)); - ret = snd_usb_autoresume(chip) ? -EIO : 0; + ret = snd_usb_lock_shutdown(chip) ? -EIO : 0; if (ret) goto error; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) { - ret = -ENODEV; - } else { - idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); - ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, size); - } - up_read(&chip->shutdown_rwsem); - snd_usb_autosuspend(chip); + snd_usb_unlock_shutdown(chip); if (ret < 0) { error: @@ -446,7 +451,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set) { struct snd_usb_audio *chip = cval->head.mixer->chip; - unsigned char buf[2]; + unsigned char buf[4]; int idx = 0, val_len, err, timeout = 10; validx += cval->idx_off; @@ -454,8 +459,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, if (cval->head.mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; } else { /* UAC_VERSION_2 */ - /* audio class v2 controls are always 2 bytes in size */ - val_len = sizeof(__u16); + val_len = uac2_ctl_value_size(cval->val_type); /* FIXME */ if (request != UAC_SET_CUR) { @@ -469,13 +473,14 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, value_set = convert_bytes_value(cval, value_set); buf[0] = value_set & 0xff; buf[1] = (value_set >> 8) & 0xff; - err = snd_usb_autoresume(chip); + buf[2] = (value_set >> 16) & 0xff; + buf[3] = (value_set >> 24) & 0xff; + + err = snd_usb_lock_shutdown(chip); if (err < 0) return -EIO; - down_read(&chip->shutdown_rwsem); + while (timeout-- > 0) { - if (chip->shutdown) - break; idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), request, @@ -490,8 +495,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, err = -EINVAL; out: - up_read(&chip->shutdown_rwsem); - snd_usb_autosuspend(chip); + snd_usb_unlock_shutdown(chip); return err; } @@ -715,15 +719,21 @@ static int check_input_term(struct mixer_build *state, int id, term->name = d->iTerminal; } else { /* UAC_VERSION_2 */ struct uac2_input_terminal_descriptor *d = p1; - term->type = le16_to_cpu(d->wTerminalType); - term->channels = d->bNrChannels; - term->chconfig = le32_to_cpu(d->bmChannelConfig); - term->name = d->iTerminal; - /* call recursively to get the clock selectors */ + /* call recursively to verify that the + * referenced clock entity is valid */ err = check_input_term(state, d->bCSourceID, term); if (err < 0) return err; + + /* save input term properties after recursion, + * to ensure they are not overriden by the + * recursion calls */ + term->id = id; + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le32_to_cpu(d->bmChannelConfig); + term->name = d->iTerminal; } return 0; case UAC_FEATURE_UNIT: { @@ -798,24 +808,25 @@ static int check_input_term(struct mixer_build *state, int id, /* feature unit control information */ struct usb_feature_control_info { const char *name; - unsigned int type; /* control type (mute, volume, etc.) */ + int type; /* data type for uac1 */ + int type_uac2; /* data type for uac2 if different from uac1, else -1 */ }; static struct usb_feature_control_info audio_feature_info[] = { - { "Mute", USB_MIXER_INV_BOOLEAN }, - { "Volume", USB_MIXER_S16 }, - { "Tone Control - Bass", USB_MIXER_S8 }, - { "Tone Control - Mid", USB_MIXER_S8 }, - { "Tone Control - Treble", USB_MIXER_S8 }, - { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */ - { "Auto Gain Control", USB_MIXER_BOOLEAN }, - { "Delay Control", USB_MIXER_U16 }, /* FIXME: U32 in UAC2 */ - { "Bass Boost", USB_MIXER_BOOLEAN }, - { "Loudness", USB_MIXER_BOOLEAN }, + { "Mute", USB_MIXER_INV_BOOLEAN, -1 }, + { "Volume", USB_MIXER_S16, -1 }, + { "Tone Control - Bass", USB_MIXER_S8, -1 }, + { "Tone Control - Mid", USB_MIXER_S8, -1 }, + { "Tone Control - Treble", USB_MIXER_S8, -1 }, + { "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemeted yet */ + { "Auto Gain Control", USB_MIXER_BOOLEAN, -1 }, + { "Delay Control", USB_MIXER_U16, USB_MIXER_U32 }, + { "Bass Boost", USB_MIXER_BOOLEAN, -1 }, + { "Loudness", USB_MIXER_BOOLEAN, -1 }, /* UAC2 specific */ - { "Input Gain Control", USB_MIXER_S16 }, - { "Input Gain Pad Control", USB_MIXER_S16 }, - { "Phase Inverter Control", USB_MIXER_BOOLEAN }, + { "Input Gain Control", USB_MIXER_S16, -1 }, + { "Input Gain Pad Control", USB_MIXER_S16, -1 }, + { "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 }, }; /* private_free callback */ @@ -1215,6 +1226,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, int readonly_mask) { struct uac_feature_unit_descriptor *desc = raw_desc; + struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; int nameid = uac_feature_unit_iFeature(desc); @@ -1240,7 +1252,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = control; cval->cmask = ctl_mask; - cval->val_type = audio_feature_info[control-1].type; + ctl_info = &audio_feature_info[control-1]; + if (state->mixer->protocol == UAC_VERSION_1) + cval->val_type = ctl_info->type; + else /* UAC_VERSION_2 */ + cval->val_type = ctl_info->type_uac2 >= 0 ? + ctl_info->type_uac2 : ctl_info->type; + if (ctl_mask == 0) { cval->channels = 1; /* master channel */ cval->master_readonly = readonly_mask; @@ -2522,7 +2540,7 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list) for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - if (cval->cached & (1 << c)) { + if (cval->cached & (1 << (c + 1))) { err = snd_usb_set_cur_mix_value(cval, c + 1, idx, cval->cache_val[idx]); if (err < 0) diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index d3268f0..3417ef3 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -33,6 +33,8 @@ enum { USB_MIXER_U8, USB_MIXER_S16, USB_MIXER_U16, + USB_MIXER_S32, + USB_MIXER_U32, }; typedef void (*usb_mixer_elem_dump_func_t)(struct snd_info_buffer *buffer, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 337c317..d3608c0 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -308,11 +308,10 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, struct snd_usb_audio *chip = mixer->chip; int err; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) { - err = -ENODEV; - goto out; - } + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + if (chip->usb_id == USB_ID(0x041e, 0x3042)) err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), 0x24, @@ -329,8 +328,7 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, value, index + 2, NULL, 0); - out: - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } @@ -441,16 +439,15 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, for (i = 0; jacks[i].name; ++i) { snd_iprintf(buffer, "%s: ", jacks[i].name); - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = 0; - else - err = snd_usb_ctl_msg(mixer->chip->dev, + err = snd_usb_lock_shutdown(mixer->chip); + if (err < 0) + return; + err = snd_usb_ctl_msg(mixer->chip->dev, usb_rcvctrlpipe(mixer->chip->dev, 0), UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, jacks[i].unitid << 8, buf, 3); - up_read(&mixer->chip->shutdown_rwsem); + snd_usb_unlock_shutdown(mixer->chip); if (err == 3 && (buf[0] == 3 || buf[0] == 6)) snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); else @@ -481,11 +478,9 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, int err; unsigned char buf[2]; - down_read(&chip->shutdown_rwsem); - if (mixer->chip->shutdown) { - err = -ENODEV; - goto out; - } + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; buf[0] = 0x01; buf[1] = value ? 0x02 : 0x01; @@ -493,8 +488,7 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 0x0400, 0x0e00, buf, 2); - out: - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } @@ -554,15 +548,14 @@ static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer, struct snd_usb_audio *chip = mixer->chip; int err; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), 0x08, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, 50, 0, &status, 1); - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } @@ -623,11 +616,9 @@ static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val) int err; unsigned char buff[3]; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) { - err = -ENODEV; - goto err; - } + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; /* Prepare for magic command to toggle clock source */ err = snd_usb_ctl_msg(chip->dev, @@ -683,7 +674,7 @@ static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val) goto err; err: - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } @@ -778,15 +769,14 @@ static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list) unsigned int pval = list->kctl->private_value; int err; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) - err = -ENODEV; - else - err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), - (pval >> 16) & 0xff, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - pval >> 24, pval & 0xffff, NULL, 0, 1000); - up_read(&chip->shutdown_rwsem); + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + pval >> 24, pval & 0xffff, NULL, 0, 1000); + snd_usb_unlock_shutdown(chip); return err; } @@ -944,18 +934,17 @@ static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list) value[0] = pval >> 24; value[1] = 0; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - pval & 0xff00, - snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8), - value, 2); - up_read(&chip->shutdown_rwsem); + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + pval & 0xff00, + snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8), + value, 2); + snd_usb_unlock_shutdown(chip); return err; } @@ -1519,11 +1508,9 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, unsigned char data[3]; int rate; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) { - err = -ENODEV; - goto end; - } + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff; ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff; @@ -1551,7 +1538,7 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, err = 0; end: - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } @@ -1562,11 +1549,9 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) u8 reg; int err; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) { - err = -ENODEV; - goto end; - } + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; reg = ((pval >> 4) & 0xf0) | (pval & 0x0f); err = snd_usb_ctl_msg(chip->dev, @@ -1594,7 +1579,7 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) goto end; end: - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } @@ -1650,11 +1635,9 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) u8 reg = list->kctl->private_value; int err; - down_read(&chip->shutdown_rwsem); - if (chip->shutdown) { - err = -ENODEV; - goto end; - } + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), @@ -1665,8 +1648,7 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) NULL, 0); - end: - up_read(&chip->shutdown_rwsem); + snd_usb_unlock_shutdown(chip); return err; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b4ef410..cdac517 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -80,7 +80,7 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream unsigned int hwptr_done; subs = (struct snd_usb_substream *)substream->runtime->private_data; - if (subs->stream->chip->shutdown) + if (atomic_read(&subs->stream->chip->shutdown)) return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); hwptr_done = subs->hwptr_done; @@ -391,6 +391,20 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, */ attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; + if ((is_playback && (attr != USB_ENDPOINT_SYNC_ASYNC)) || + (!is_playback && (attr != USB_ENDPOINT_SYNC_ADAPTIVE))) { + + /* + * In these modes the notion of sync_endpoint is irrelevant. + * Reset pointers to avoid using stale data from previously + * used settings, e.g. when configuration and endpoints were + * changed + */ + + subs->sync_endpoint = NULL; + subs->data_endpoint->sync_master = NULL; + } + err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr); if (err < 0) return err; @@ -398,10 +412,17 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, if (altsd->bNumEndpoints < 2) return 0; - if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) || + if ((is_playback && (attr == USB_ENDPOINT_SYNC_SYNC || + attr == USB_ENDPOINT_SYNC_ADAPTIVE)) || (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE)) return 0; + /* + * In case of illegal SYNC_NONE for OUT endpoint, we keep going to see + * if we don't find a sync endpoint, as on M-Audio Transit. In case of + * error fall back to SYNC mode and don't create sync endpoint + */ + /* check sync-pipe endpoint */ /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking @@ -415,6 +436,8 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, get_endpoint(alts, 1)->bmAttributes, get_endpoint(alts, 1)->bLength, get_endpoint(alts, 1)->bSynchAddress); + if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) + return 0; return -EINVAL; } ep = get_endpoint(alts, 1)->bEndpointAddress; @@ -425,6 +448,8 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, "%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n", fmt->iface, fmt->altsetting, is_playback, ep, get_endpoint(alts, 0)->bSynchAddress); + if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) + return 0; return -EINVAL; } @@ -436,8 +461,11 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, implicit_fb ? SND_USB_ENDPOINT_TYPE_DATA : SND_USB_ENDPOINT_TYPE_SYNC); - if (!subs->sync_endpoint) + if (!subs->sync_endpoint) { + if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) + return 0; return -EINVAL; + } subs->data_endpoint->sync_master = subs->sync_endpoint; @@ -707,12 +735,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - down_read(&subs->stream->chip->shutdown_rwsem); - if (subs->stream->chip->shutdown) - ret = -ENODEV; - else - ret = set_format(subs, fmt); - up_read(&subs->stream->chip->shutdown_rwsem); + ret = snd_usb_lock_shutdown(subs->stream->chip); + if (ret < 0) + return ret; + ret = set_format(subs, fmt); + snd_usb_unlock_shutdown(subs->stream->chip); if (ret < 0) return ret; @@ -735,13 +762,12 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_audiofmt = NULL; subs->cur_rate = 0; subs->period_bytes = 0; - down_read(&subs->stream->chip->shutdown_rwsem); - if (!subs->stream->chip->shutdown) { + if (!snd_usb_lock_shutdown(subs->stream->chip)) { stop_endpoints(subs, true); snd_usb_endpoint_deactivate(subs->sync_endpoint); snd_usb_endpoint_deactivate(subs->data_endpoint); + snd_usb_unlock_shutdown(subs->stream->chip); } - up_read(&subs->stream->chip->shutdown_rwsem); return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -763,11 +789,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) return -ENXIO; } - down_read(&subs->stream->chip->shutdown_rwsem); - if (subs->stream->chip->shutdown) { - ret = -ENODEV; - goto unlock; - } + ret = snd_usb_lock_shutdown(subs->stream->chip); + if (ret < 0) + return ret; if (snd_BUG_ON(!subs->data_endpoint)) { ret = -EIO; goto unlock; @@ -816,7 +840,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) ret = start_endpoints(subs, true); unlock: - up_read(&subs->stream->chip->shutdown_rwsem); + snd_usb_unlock_shutdown(subs->stream->chip); return ret; } @@ -1218,9 +1242,11 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) stop_endpoints(subs, true); - if (!as->chip->shutdown && subs->interface >= 0) { + if (subs->interface >= 0 && + !snd_usb_lock_shutdown(subs->stream->chip)) { usb_set_interface(subs->dev, subs->interface, 0); subs->interface = -1; + snd_usb_unlock_shutdown(subs->stream->chip); } subs->pcm_substream = NULL; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 5f761ab..0ac89e2 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -46,14 +46,14 @@ static inline unsigned get_high_speed_hz(unsigned int usb_rate) static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_usb_audio *chip = entry->private_data; - if (!chip->shutdown) + if (!atomic_read(&chip->shutdown)) snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum); } static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_usb_audio *chip = entry->private_data; - if (!chip->shutdown) + if (!atomic_read(&chip->shutdown)) snd_iprintf(buffer, "%04x:%04x\n", USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 91d0380..33a1764 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -37,11 +37,11 @@ struct snd_usb_audio { struct usb_interface *pm_intf; u32 usb_id; struct mutex mutex; - struct rw_semaphore shutdown_rwsem; - unsigned int shutdown:1; - unsigned int probing:1; - unsigned int in_pm:1; unsigned int autosuspended:1; + atomic_t active; + atomic_t shutdown; + atomic_t usage_count; + wait_queue_head_t shutdown_wait; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ int num_interfaces; @@ -115,4 +115,7 @@ struct snd_usb_audio_quirk { #define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16)) #define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24)) +int snd_usb_lock_shutdown(struct snd_usb_audio *chip); +void snd_usb_unlock_shutdown(struct snd_usb_audio *chip); + #endif /* __USBAUDIO_H */ |