summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-25 17:15:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-25 17:15:18 -0700
commit4570a37169d4b44d316f40b2ccc681dc93fedc7b (patch)
treecafffb586c60dddfb04b8619fa1ae0e859600de7 /sound
parentf7b08217c755e88a29b5bd53b4a1d10cd8b3c5f8 (diff)
parent60b93030b44a8c2cd015cebe5624fd7552ec67ec (diff)
Merge tag 'sound-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "It was a busy development cycle at this time, as you can see a wide range of changes in diffstat. There are no big changes but many refactoring and improvements. Here we go some highlights: ALSA core: - Procfs codes were cleaned up to use seq_file - Procfs can be opt out via Kconfig (only for EXPERT) - Two types of jack API were unified finally; now both kctl and input jack devs are handled via a single function call. HD-audio: - Continued code restructuring for the future ASoC driver; now HDA controller driver is split to a core helper module. - Preliminary codes for Skylake audio support in HDA core. - Proper i915 gfx power well management for SKL & co - Enabled runtime PM as default for Intel HDMI/DP codecs - Newer Tegra chip supports - More quirks for Dell headsets, Alienware (with CA0132), etc. - A couple of DRM ELD helper API functions ASoC: - Support for loading ASoC topology maps from firmware, intended to be used to allow self-describing DSP firmware images to be built which can map controls added by the DSP to userspace without the kernel needing to know about individual DSP firmwares - Lots of refactoring to avoid direct access to snd_soc_codec where it's not needed supporting future refactoring - Big refactoring, cleanup and enhancement for the Wolfson ADSP driver - Cleanup series for TI TAS2552 and R-CAR drivers - Fixes and improvements on RT56xx codecs - Support for TI TAS571x power amplifiers - Support for Qualcomm APQ8016 and ZTE ZX296702 SoCs - Support for x86 systems with RT5650 and Qualcomm Storm - Support for Mediatek AFE (Audio Front End) unit - Other various small fixes to ASoC codec drivers Firewire: - Enhanced to allow non-blocking streams to use timestamp synchronization - Improve support for DM1500 and BeBoBv3 Misc: - Cleanup of old pci API functions over all PCI sound drivers - Fix long-standing regression of the old powermac i2c setup" * tag 'sound-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (533 commits) ALSA: pcm: Fix pcm_class sysfs output ALSA: hda-beep: Update authors dead email address ASoC: wm_adsp: Move DSP Rate controls into the codec ASoC: wm8995: Fix setting sysclk for WM8995_SYSCLK_MCLK2 case ALSA: hda: provide default bus io ops extended hdac ALSA: hda: add hda link cleanup routine ALSA: hda: add hdac_ext stream creation and cleanup routines ASoC: rsrc-card: remove unused ret ALSA: HDAC: move SND_HDA_PREALLOC_SIZE to core ASoC: mediatek: Add machine driver for rt5650 rt5676 codec ASoC: mediatek: Add machine driver for MAX98090 codec ASoC: mediatek: Add AFE platform driver ASoC: rsnd: remove io from rsnd_mod ASoC: rsnd: move rsnd_mod_is_working() to rsnd_io_is_working() ASoC: rsnd: don't use rsnd_mod_to_io() on snd_kcontrol ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_src_xxx() ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_ssi_xxx() ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_dma_xxx() ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_get_adinr() ASoC: rsnd: add common interrupt handler for SSI/SRC/DMA ...
Diffstat (limited to 'sound')
-rw-r--r--sound/aoa/soundbus/core.c4
-rw-r--r--sound/aoa/soundbus/soundbus.h2
-rw-r--r--sound/aoa/soundbus/sysfs.c13
-rw-r--r--sound/core/Kconfig20
-rw-r--r--sound/core/Makefile13
-rw-r--r--sound/core/ctljack.c41
-rw-r--r--sound/core/hwdep.c6
-rw-r--r--sound/core/info.c833
-rw-r--r--sound/core/info_oss.c29
-rw-r--r--sound/core/init.c59
-rw-r--r--sound/core/jack.c146
-rw-r--r--sound/core/oss/mixer_oss.c6
-rw-r--r--sound/core/pcm.c12
-rw-r--r--sound/core/pcm_drm_eld.c99
-rw-r--r--sound/core/pcm_iec958.c95
-rw-r--r--sound/core/seq/Makefile3
-rw-r--r--sound/core/seq/oss/seq_oss.c6
-rw-r--r--sound/core/seq/oss/seq_oss_init.c5
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c4
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c4
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c4
-rw-r--r--sound/core/seq/seq_clientmgr.c4
-rw-r--r--sound/core/seq/seq_device.c6
-rw-r--r--sound/core/seq/seq_info.c19
-rw-r--r--sound/core/seq/seq_info.h2
-rw-r--r--sound/core/seq/seq_queue.c4
-rw-r--r--sound/core/seq/seq_timer.c4
-rw-r--r--sound/core/sound.c28
-rw-r--r--sound/core/sound_oss.c34
-rw-r--r--sound/core/timer.c4
-rw-r--r--sound/drivers/aloop.c8
-rw-r--r--sound/drivers/dummy.c18
-rw-r--r--sound/drivers/opl4/Makefile3
-rw-r--r--sound/drivers/opl4/opl4_lib.c4
-rw-r--r--sound/drivers/opl4/opl4_local.h7
-rw-r--r--sound/drivers/opl4/opl4_proc.c4
-rw-r--r--sound/firewire/Kconfig2
-rw-r--r--sound/firewire/amdtp.c271
-rw-r--r--sound/firewire/amdtp.h4
-rw-r--r--sound/firewire/bebob/bebob.c17
-rw-r--r--sound/firewire/bebob/bebob.h20
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c33
-rw-r--r--sound/firewire/bebob/bebob_maudio.c23
-rw-r--r--sound/firewire/bebob/bebob_midi.c8
-rw-r--r--sound/firewire/bebob/bebob_pcm.c14
-rw-r--r--sound/firewire/bebob/bebob_proc.c22
-rw-r--r--sound/firewire/bebob/bebob_stream.c150
-rw-r--r--sound/firewire/bebob/bebob_terratec.c30
-rw-r--r--sound/firewire/bebob/bebob_yamaha.c20
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c10
-rw-r--r--sound/hda/Kconfig26
-rw-r--r--sound/hda/Makefile8
-rw-r--r--sound/hda/ext/Makefile3
-rw-r--r--sound/hda/ext/hdac_ext_bus.c174
-rw-r--r--sound/hda/ext/hdac_ext_controller.c288
-rw-r--r--sound/hda/ext/hdac_ext_stream.c452
-rw-r--r--sound/hda/hda_bus_type.c41
-rw-r--r--sound/hda/hdac_bus.c20
-rw-r--r--sound/hda/hdac_controller.c507
-rw-r--r--sound/hda/hdac_device.c315
-rw-r--r--sound/hda/hdac_i915.c196
-rw-r--r--sound/hda/hdac_stream.c697
-rw-r--r--sound/hda/trace.h27
-rw-r--r--sound/i2c/other/ak4xxx-adda.c4
-rw-r--r--sound/isa/gus/gus_mixer.c9
-rw-r--r--sound/oss/ad1848.c2
-rw-r--r--sound/oss/msnd_pinnacle.c3
-rw-r--r--sound/oss/sb_audio.c8
-rw-r--r--sound/pci/ac97/Makefile2
-rw-r--r--sound/pci/ac97/ac97_local.h2
-rw-r--r--sound/pci/ad1889.c4
-rw-r--r--sound/pci/ak4531_codec.c6
-rw-r--r--sound/pci/ali5451/ali5451.c4
-rw-r--r--sound/pci/als300.c4
-rw-r--r--sound/pci/als4000.c4
-rw-r--r--sound/pci/atiixp.c4
-rw-r--r--sound/pci/atiixp_modem.c4
-rw-r--r--sound/pci/au88x0/au88x0.c4
-rw-r--r--sound/pci/aw2/aw2-alsa.c4
-rw-r--r--sound/pci/azt3328.c4
-rw-r--r--sound/pci/ca0106/Makefile3
-rw-r--r--sound/pci/ca0106/ca0106_main.c6
-rw-r--r--sound/pci/ca0106/ca0106_proc.c4
-rw-r--r--sound/pci/cmipci.c5
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c4
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h4
-rw-r--r--sound/pci/cs46xx/dsp_spos.c4
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c6
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c4
-rw-r--r--sound/pci/ctxfi/cthw20k1.c4
-rw-r--r--sound/pci/ctxfi/cthw20k2.c4
-rw-r--r--sound/pci/emu10k1/Makefile3
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c6
-rw-r--r--sound/pci/emu10k1/emuproc.c2
-rw-r--r--sound/pci/es1938.c4
-rw-r--r--sound/pci/es1968.c4
-rw-r--r--sound/pci/hda/Kconfig31
-rw-r--r--sound/pci/hda/Makefile9
-rw-r--r--sound/pci/hda/hda_beep.c2
-rw-r--r--sound/pci/hda/hda_beep.h2
-rw-r--r--sound/pci/hda/hda_bind.c10
-rw-r--r--sound/pci/hda/hda_codec.c425
-rw-r--r--sound/pci/hda/hda_codec.h87
-rw-r--r--sound/pci/hda/hda_controller.c1347
-rw-r--r--sound/pci/hda/hda_controller.h272
-rw-r--r--sound/pci/hda/hda_controller_trace.h98
-rw-r--r--sound/pci/hda/hda_eld.c4
-rw-r--r--sound/pci/hda/hda_i915.c196
-rw-r--r--sound/pci/hda/hda_intel.c382
-rw-r--r--sound/pci/hda/hda_intel.h26
-rw-r--r--sound/pci/hda/hda_intel_trace.h55
-rw-r--r--sound/pci/hda/hda_jack.c90
-rw-r--r--sound/pci/hda/hda_jack.h5
-rw-r--r--sound/pci/hda/hda_local.h4
-rw-r--r--sound/pci/hda/hda_tegra.c102
-rw-r--r--sound/pci/hda/patch_analog.c3
-rw-r--r--sound/pci/hda/patch_ca0110.c3
-rw-r--r--sound/pci/hda/patch_ca0132.c133
-rw-r--r--sound/pci/hda/patch_cirrus.c12
-rw-r--r--sound/pci/hda/patch_cmedia.c4
-rw-r--r--sound/pci/hda/patch_conexant.c3
-rw-r--r--sound/pci/hda/patch_hdmi.c193
-rw-r--r--sound/pci/hda/patch_realtek.c101
-rw-r--r--sound/pci/hda/patch_sigmatel.c22
-rw-r--r--sound/pci/hda/patch_via.c21
-rw-r--r--sound/pci/ice1712/ice1712.c4
-rw-r--r--sound/pci/ice1712/quartet.c7
-rw-r--r--sound/pci/intel8x0.c4
-rw-r--r--sound/pci/intel8x0m.c5
-rw-r--r--sound/pci/lx6464es/lx6464es.c18
-rw-r--r--sound/pci/maestro3.c4
-rw-r--r--sound/pci/mixart/mixart.c2
-rw-r--r--sound/pci/oxygen/oxygen_lib.c4
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c2
-rw-r--r--sound/pci/pcxhr/pcxhr.c2
-rw-r--r--sound/pci/sis7019.c10
-rw-r--r--sound/pci/sonicvibes.c4
-rw-r--r--sound/pci/trident/trident_main.c4
-rw-r--r--sound/ppc/keywest.c36
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/Makefile3
-rw-r--r--sound/soc/atmel/Kconfig37
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c3
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c10
-rw-r--r--sound/soc/au1x/db1200.c2
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c1
-rw-r--r--sound/soc/codecs/88pm860x-codec.c19
-rw-r--r--sound/soc/codecs/Kconfig17
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/ab8500-codec.c20
-rw-r--r--sound/soc/codecs/ac97.c8
-rw-r--r--sound/soc/codecs/ad1836.c2
-rw-r--r--sound/soc/codecs/adau1373.c1
-rw-r--r--sound/soc/codecs/adau1701.c126
-rw-r--r--sound/soc/codecs/adau1761.c27
-rw-r--r--sound/soc/codecs/adau1781.c10
-rw-r--r--sound/soc/codecs/adau17x1.c20
-rw-r--r--sound/soc/codecs/adau1977.c14
-rw-r--r--sound/soc/codecs/adav80x.c11
-rw-r--r--sound/soc/codecs/ak4535.c1
-rw-r--r--sound/soc/codecs/ak4641.c3
-rw-r--r--sound/soc/codecs/ak4642.c1
-rw-r--r--sound/soc/codecs/ak4671.c1
-rw-r--r--sound/soc/codecs/alc5623.c3
-rw-r--r--sound/soc/codecs/alc5632.c1
-rw-r--r--sound/soc/codecs/arizona.c201
-rw-r--r--sound/soc/codecs/arizona.h19
-rw-r--r--sound/soc/codecs/bt-sco.c11
-rw-r--r--sound/soc/codecs/cq93vc.c1
-rw-r--r--sound/soc/codecs/cs35l32.c1
-rw-r--r--sound/soc/codecs/cs4265.c1
-rw-r--r--sound/soc/codecs/cs42l52.c5
-rw-r--r--sound/soc/codecs/cs42l56.c5
-rw-r--r--sound/soc/codecs/cs42l73.c3
-rw-r--r--sound/soc/codecs/cs42xx8.c2
-rw-r--r--sound/soc/codecs/cx20442.c6
-rw-r--r--sound/soc/codecs/da7213.c3
-rw-r--r--sound/soc/codecs/da732x.c4
-rw-r--r--sound/soc/codecs/da9055.c3
-rw-r--r--sound/soc/codecs/es8328.c3
-rw-r--r--sound/soc/codecs/isabelle.c2
-rw-r--r--sound/soc/codecs/jz4740.c4
-rw-r--r--sound/soc/codecs/lm4857.c114
-rw-r--r--sound/soc/codecs/lm49453.c4
-rw-r--r--sound/soc/codecs/max98088.c3
-rw-r--r--sound/soc/codecs/max98090.c38
-rw-r--r--sound/soc/codecs/max98095.c24
-rw-r--r--sound/soc/codecs/max98357a.c3
-rw-r--r--sound/soc/codecs/max9850.c3
-rw-r--r--sound/soc/codecs/max98925.c2
-rw-r--r--sound/soc/codecs/ml26124.c61
-rw-r--r--sound/soc/codecs/pcm512x.c8
-rw-r--r--sound/soc/codecs/rl6347a.c128
-rw-r--r--sound/soc/codecs/rl6347a.h32
-rw-r--r--sound/soc/codecs/rt286.c130
-rw-r--r--sound/soc/codecs/rt5631.c5
-rw-r--r--sound/soc/codecs/rt5640.c21
-rw-r--r--sound/soc/codecs/rt5645.c1112
-rw-r--r--sound/soc/codecs/rt5645.h31
-rw-r--r--sound/soc/codecs/rt5651.c5
-rw-r--r--sound/soc/codecs/rt5670.c31
-rw-r--r--sound/soc/codecs/rt5677.c157
-rw-r--r--sound/soc/codecs/rt5677.h15
-rw-r--r--sound/soc/codecs/sgtl5000.c56
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c2
-rw-r--r--sound/soc/codecs/sn95031.c12
-rw-r--r--sound/soc/codecs/ssm2518.c9
-rw-r--r--sound/soc/codecs/ssm2602.c5
-rw-r--r--sound/soc/codecs/ssm4567.c9
-rw-r--r--sound/soc/codecs/sta32x.c19
-rw-r--r--sound/soc/codecs/sta350.c9
-rw-r--r--sound/soc/codecs/sta529.c8
-rw-r--r--sound/soc/codecs/stac9766.c3
-rw-r--r--sound/soc/codecs/tas2552.c430
-rw-r--r--sound/soc/codecs/tas2552.h153
-rw-r--r--sound/soc/codecs/tas571x.c514
-rw-r--r--sound/soc/codecs/tas571x.h33
-rw-r--r--sound/soc/codecs/tlv320aic23.c1
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c11
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c1
-rw-r--r--sound/soc/codecs/tlv320aic3x.c10
-rw-r--r--sound/soc/codecs/tlv320dac33.c5
-rw-r--r--sound/soc/codecs/ts3a227e.c15
-rw-r--r--sound/soc/codecs/twl4030.c3
-rw-r--r--sound/soc/codecs/twl6040.c9
-rw-r--r--sound/soc/codecs/uda134x.c4
-rw-r--r--sound/soc/codecs/uda1380.c6
-rw-r--r--sound/soc/codecs/wm0010.c6
-rw-r--r--sound/soc/codecs/wm1250-ev1.c2
-rw-r--r--sound/soc/codecs/wm2200.c2
-rw-r--r--sound/soc/codecs/wm5100.c12
-rw-r--r--sound/soc/codecs/wm5102.c73
-rw-r--r--sound/soc/codecs/wm5110.c29
-rw-r--r--sound/soc/codecs/wm8350.c3
-rw-r--r--sound/soc/codecs/wm8400.c3
-rw-r--r--sound/soc/codecs/wm8510.c3
-rw-r--r--sound/soc/codecs/wm8523.c29
-rw-r--r--sound/soc/codecs/wm8580.c3
-rw-r--r--sound/soc/codecs/wm8711.c3
-rw-r--r--sound/soc/codecs/wm8728.c3
-rw-r--r--sound/soc/codecs/wm8731.c8
-rw-r--r--sound/soc/codecs/wm8737.c11
-rw-r--r--sound/soc/codecs/wm8741.c190
-rw-r--r--sound/soc/codecs/wm8741.h10
-rw-r--r--sound/soc/codecs/wm8750.c3
-rw-r--r--sound/soc/codecs/wm8753.c3
-rw-r--r--sound/soc/codecs/wm8770.c3
-rw-r--r--sound/soc/codecs/wm8776.c3
-rw-r--r--sound/soc/codecs/wm8804.c2
-rw-r--r--sound/soc/codecs/wm8900.c13
-rw-r--r--sound/soc/codecs/wm8903.c4
-rw-r--r--sound/soc/codecs/wm8903.h2
-rw-r--r--sound/soc/codecs/wm8904.c5
-rw-r--r--sound/soc/codecs/wm8940.c6
-rw-r--r--sound/soc/codecs/wm8955.c7
-rw-r--r--sound/soc/codecs/wm8960.c127
-rw-r--r--sound/soc/codecs/wm8961.c6
-rw-r--r--sound/soc/codecs/wm8962.c21
-rw-r--r--sound/soc/codecs/wm8971.c3
-rw-r--r--sound/soc/codecs/wm8974.c3
-rw-r--r--sound/soc/codecs/wm8978.c7
-rw-r--r--sound/soc/codecs/wm8983.c3
-rw-r--r--sound/soc/codecs/wm8985.c3
-rw-r--r--sound/soc/codecs/wm8988.c3
-rw-r--r--sound/soc/codecs/wm8990.c5
-rw-r--r--sound/soc/codecs/wm8991.c3
-rw-r--r--sound/soc/codecs/wm8993.c12
-rw-r--r--sound/soc/codecs/wm8994.c68
-rw-r--r--sound/soc/codecs/wm8995.c8
-rw-r--r--sound/soc/codecs/wm8996.c23
-rw-r--r--sound/soc/codecs/wm8997.c18
-rw-r--r--sound/soc/codecs/wm9081.c4
-rw-r--r--sound/soc/codecs/wm9090.c6
-rw-r--r--sound/soc/codecs/wm9712.c3
-rw-r--r--sound/soc/codecs/wm9713.c7
-rw-r--r--sound/soc/codecs/wm_adsp.c1452
-rw-r--r--sound/soc/codecs/wm_adsp.h35
-rw-r--r--sound/soc/codecs/wm_hubs.c4
-rw-r--r--sound/soc/codecs/wmfw.h44
-rw-r--r--sound/soc/davinci/davinci-mcasp.c239
-rw-r--r--sound/soc/davinci/davinci-mcasp.h5
-rw-r--r--sound/soc/fsl/fsl_dma.c4
-rw-r--r--sound/soc/fsl/fsl_sai.c144
-rw-r--r--sound/soc/fsl/fsl_sai.h9
-rw-r--r--sound/soc/fsl/fsl_spdif.c10
-rw-r--r--sound/soc/fsl/fsl_ssi.c7
-rw-r--r--sound/soc/fsl/imx-audmux.c2
-rw-r--r--sound/soc/fsl/imx-mc13783.c6
-rw-r--r--sound/soc/fsl/imx-wm8962.c2
-rw-r--r--sound/soc/generic/simple-card.c34
-rw-r--r--sound/soc/intel/Kconfig19
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c187
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h9
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c47
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h2
-rw-r--r--sound/soc/intel/atom/sst/sst.c4
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c4
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c2
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.c11
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c348
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c118
-rw-r--r--sound/soc/intel/common/sst-acpi.c2
-rw-r--r--sound/soc/intel/common/sst-ipc.c34
-rw-r--r--sound/soc/intel/common/sst-ipc.h7
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c12
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c32
-rw-r--r--sound/soc/mediatek/Kconfig30
-rw-r--r--sound/soc/mediatek/Makefile5
-rw-r--r--sound/soc/mediatek/mt8173-max98090.c213
-rw-r--r--sound/soc/mediatek/mt8173-rt5650-rt5676.c278
-rw-r--r--sound/soc/mediatek/mtk-afe-common.h109
-rw-r--r--sound/soc/mediatek/mtk-afe-pcm.c1233
-rw-r--r--sound/soc/omap/Kconfig5
-rw-r--r--sound/soc/omap/omap-twl4030.c3
-rw-r--r--sound/soc/omap/rx51.c40
-rw-r--r--sound/soc/pxa/brownstone.c25
-rw-r--r--sound/soc/pxa/poodle.c19
-rw-r--r--sound/soc/pxa/tosa.c13
-rw-r--r--sound/soc/pxa/z2.c9
-rw-r--r--sound/soc/qcom/Kconfig28
-rw-r--r--sound/soc/qcom/Makefile6
-rw-r--r--sound/soc/qcom/apq8016_sbc.c198
-rw-r--r--sound/soc/qcom/lpass-apq8016.c242
-rw-r--r--sound/soc/qcom/lpass-cpu.c240
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c109
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h (renamed from sound/soc/qcom/lpass-lpaif-ipq806x.h)92
-rw-r--r--sound/soc/qcom/lpass-platform.c202
-rw-r--r--sound/soc/qcom/lpass.h51
-rw-r--r--sound/soc/qcom/storm.c26
-rw-r--r--sound/soc/samsung/Kconfig15
-rw-r--r--sound/soc/samsung/i2s.c2
-rw-r--r--sound/soc/samsung/lowland.c2
-rw-r--r--sound/soc/samsung/smartq_wm8987.c6
-rw-r--r--sound/soc/samsung/smdk_wm8994.c3
-rw-r--r--sound/soc/samsung/speyside.c2
-rw-r--r--sound/soc/sh/rcar/core.c139
-rw-r--r--sound/soc/sh/rcar/dma.c113
-rw-r--r--sound/soc/sh/rcar/dvc.c30
-rw-r--r--sound/soc/sh/rcar/rsnd.h113
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c439
-rw-r--r--sound/soc/sh/rcar/src.c130
-rw-r--r--sound/soc/sh/rcar/ssi.c160
-rw-r--r--sound/soc/soc-core.c67
-rw-r--r--sound/soc/soc-dapm.c349
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c25
-rw-r--r--sound/soc/soc-jack.c12
-rw-r--r--sound/soc/soc-pcm.c47
-rw-r--r--sound/soc/soc-topology.c1826
-rw-r--r--sound/soc/ux500/mop500_ab8500.c4
-rw-r--r--sound/soc/ux500/ux500_pcm.c1
-rw-r--r--sound/soc/zte/Kconfig17
-rw-r--r--sound/soc/zte/Makefile2
-rw-r--r--sound/soc/zte/zx296702-i2s.c436
-rw-r--r--sound/soc/zte/zx296702-spdif.c365
-rw-r--r--sound/sound_firmware.c4
-rw-r--r--sound/synth/emux/Makefile5
-rw-r--r--sound/synth/emux/emux.c4
-rw-r--r--sound/synth/emux/emux_proc.c4
-rw-r--r--sound/synth/emux/emux_voice.h6
-rw-r--r--sound/usb/bcd2000/bcd2000.c2
-rw-r--r--sound/usb/mixer.c6
362 files changed, 17238 insertions, 6578 deletions
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c
index 7487eb76e034..3edf736319fe 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -150,6 +150,8 @@ static int soundbus_device_resume(struct device * dev)
#endif /* CONFIG_PM */
+/* soundbus_dev_attrs is declared in sysfs.c */
+ATTRIBUTE_GROUPS(soundbus_dev);
static struct bus_type soundbus_bus_type = {
.name = "aoa-soundbus",
.probe = soundbus_probe,
@@ -160,7 +162,7 @@ static struct bus_type soundbus_bus_type = {
.suspend = soundbus_device_suspend,
.resume = soundbus_device_resume,
#endif
- .dev_attrs = soundbus_dev_attrs,
+ .dev_groups = soundbus_dev_groups,
};
int soundbus_add_one(struct soundbus_dev *dev)
diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h
index adecbf36f4f6..21e756cf2824 100644
--- a/sound/aoa/soundbus/soundbus.h
+++ b/sound/aoa/soundbus/soundbus.h
@@ -199,6 +199,6 @@ struct soundbus_driver {
extern int soundbus_register_driver(struct soundbus_driver *drv);
extern void soundbus_unregister_driver(struct soundbus_driver *drv);
-extern struct device_attribute soundbus_dev_attrs[];
+extern struct attribute *soundbus_dev_attrs[];
#endif /* __SOUNDBUS_H */
diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c
index e0980b5c2cd8..5b2d51d99768 100644
--- a/sound/aoa/soundbus/sysfs.c
+++ b/sound/aoa/soundbus/sysfs.c
@@ -30,13 +30,16 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
return length;
}
+static DEVICE_ATTR_RO(modalias);
soundbus_config_of_attr (name, "%s\n");
+static DEVICE_ATTR_RO(name);
soundbus_config_of_attr (type, "%s\n");
+static DEVICE_ATTR_RO(type);
-struct device_attribute soundbus_dev_attrs[] = {
- __ATTR_RO(name),
- __ATTR_RO(type),
- __ATTR_RO(modalias),
- __ATTR_NULL
+struct attribute *soundbus_dev_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_type.attr,
+ &dev_attr_modalias.attr,
+ NULL,
};
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 313f22e9d929..6c96feeaf01e 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -6,6 +6,12 @@ config SND_PCM
tristate
select SND_TIMER
+config SND_PCM_ELD
+ bool
+
+config SND_PCM_IEC958
+ bool
+
config SND_DMAENGINE_PCM
tristate
@@ -176,9 +182,18 @@ config SND_SUPPORT_OLD_API
Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
or older).
+config SND_PROC_FS
+ bool "Sound Proc FS Support" if EXPERT
+ depends on PROC_FS
+ default y
+ help
+ Say 'N' to disable Sound proc FS, which may reduce code size about
+ 9KB on x86_64 platform.
+ If unsure say Y.
+
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
- depends on PROC_FS
+ depends on SND_PROC_FS
default y
help
Say Y here to include code for verbose procfs contents (provides
@@ -221,9 +236,6 @@ config SND_PCM_XRUN_DEBUG
config SND_VMASTER
bool
-config SND_KCTL_JACK
- bool
-
config SND_DMA_SGBUF
def_bool y
depends on X86
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 4daf2f58261c..3354f91e003a 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -3,16 +3,21 @@
# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
+snd-y := sound.o init.o memory.o control.o misc.o device.o
+ifneq ($(CONFIG_SND_PROC_FS),)
+snd-y += info.o
+snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
+endif
snd-$(CONFIG_ISA_DMA_API) += isadma.o
-snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
+snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
-snd-$(CONFIG_SND_KCTL_JACK) += ctljack.o
-snd-$(CONFIG_SND_JACK) += jack.o
+snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o memalloc.o
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
+snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
+snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
# for trace-points
CFLAGS_pcm_lib.o := -I$(src)
diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c
index e4b38fbe51da..9149a4aefa95 100644
--- a/sound/core/ctljack.c
+++ b/sound/core/ctljack.c
@@ -31,19 +31,49 @@ static struct snd_kcontrol_new jack_detect_kctl = {
.get = jack_detect_kctl_get,
};
+static int get_available_index(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id sid;
+
+ memset(&sid, 0, sizeof(sid));
+
+ sid.index = 0;
+ sid.iface = SNDRV_CTL_ELEM_IFACE_CARD;
+ strlcpy(sid.name, name, sizeof(sid.name));
+
+ while (snd_ctl_find_id(card, &sid))
+ sid.index++;
+
+ return sid.index;
+}
+
+static void jack_kctl_name_gen(char *name, const char *src_name, int size)
+{
+ size_t count = strlen(src_name);
+ bool need_cat = true;
+
+ /* remove redundant " Jack" from src_name */
+ if (count >= 5)
+ need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false;
+
+ snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name);
+
+}
+
struct snd_kcontrol *
-snd_kctl_jack_new(const char *name, int idx, void *private_data)
+snd_kctl_jack_new(const char *name, struct snd_card *card)
{
struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(&jack_detect_kctl, private_data);
+
+ kctl = snd_ctl_new1(&jack_detect_kctl, NULL);
if (!kctl)
return NULL;
- snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name);
- kctl->id.index = idx;
+
+ jack_kctl_name_gen(kctl->id.name, name, sizeof(kctl->id.name));
+ kctl->id.index = get_available_index(card, kctl->id.name);
kctl->private_value = 0;
return kctl;
}
-EXPORT_SYMBOL_GPL(snd_kctl_jack_new);
void snd_kctl_jack_report(struct snd_card *card,
struct snd_kcontrol *kctl, bool status)
@@ -53,4 +83,3 @@ void snd_kctl_jack_report(struct snd_card *card,
kctl->private_value = status;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
}
-EXPORT_SYMBOL_GPL(snd_kctl_jack_report);
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 51692c8a39ea..36d2416f90d9 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -484,7 +484,7 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -521,10 +521,10 @@ static void __exit snd_hwdep_proc_done(void)
{
snd_info_free_entry(snd_hwdep_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_hwdep_proc_init()
#define snd_hwdep_proc_done()
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
diff --git a/sound/core/info.c b/sound/core/info.c
index 9f404e965ea2..895362a696c9 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -33,12 +33,6 @@
#include <linux/mutex.h>
#include <stdarg.h>
-/*
- *
- */
-
-#ifdef CONFIG_PROC_FS
-
int snd_info_check_reserved_words(const char *str)
{
static char *reserved[] =
@@ -78,81 +72,51 @@ struct snd_info_private_data {
};
static int snd_info_version_init(void);
-static int snd_info_version_done(void);
static void snd_info_disconnect(struct snd_info_entry *entry);
+/*
-/* resize the proc r/w buffer */
-static int resize_info_buffer(struct snd_info_buffer *buffer,
- unsigned int nsize)
-{
- char *nbuf;
+ */
- nsize = PAGE_ALIGN(nsize);
- nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO);
- if (! nbuf)
- return -ENOMEM;
+static struct snd_info_entry *snd_proc_root;
+struct snd_info_entry *snd_seq_root;
+EXPORT_SYMBOL(snd_seq_root);
- buffer->buffer = nbuf;
- buffer->len = nsize;
- return 0;
-}
+#ifdef CONFIG_SND_OSSEMUL
+struct snd_info_entry *snd_oss_root;
+#endif
-/**
- * snd_iprintf - printf on the procfs buffer
- * @buffer: the procfs buffer
- * @fmt: the printf format
- *
- * Outputs the string on the procfs buffer just like printf().
- *
- * Return: The size of output string, or a negative error code.
- */
-int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
+static int alloc_info_private(struct snd_info_entry *entry,
+ struct snd_info_private_data **ret)
{
- va_list args;
- int len, res;
- int err = 0;
+ struct snd_info_private_data *data;
- might_sleep();
- if (buffer->stop || buffer->error)
- return 0;
- len = buffer->len - buffer->size;
- va_start(args, fmt);
- for (;;) {
- va_list ap;
- va_copy(ap, args);
- res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
- va_end(ap);
- if (res < len)
- break;
- err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
- if (err < 0)
- break;
- len = buffer->len - buffer->size;
+ if (!entry || !entry->p)
+ return -ENODEV;
+ if (!try_module_get(entry->module))
+ return -EFAULT;
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ module_put(entry->module);
+ return -ENOMEM;
}
- va_end(args);
-
- if (err < 0)
- return err;
- buffer->curr += res;
- buffer->size += res;
- return res;
+ data->entry = entry;
+ *ret = data;
+ return 0;
}
-EXPORT_SYMBOL(snd_iprintf);
+static bool valid_pos(loff_t pos, size_t count)
+{
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return false;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return false;
+ return true;
+}
/*
-
+ * file ops for binary proc files
*/
-
-static struct proc_dir_entry *snd_proc_root;
-struct snd_info_entry *snd_seq_root;
-EXPORT_SYMBOL(snd_seq_root);
-
-#ifdef CONFIG_SND_OSSEMUL
-struct snd_info_entry *snd_oss_root;
-#endif
-
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
struct snd_info_private_data *data;
@@ -162,17 +126,14 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
data = file->private_data;
entry = data->entry;
mutex_lock(&entry->access);
- if (entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->llseek) {
+ if (entry->c.ops->llseek) {
offset = entry->c.ops->llseek(entry,
data->file_private_data,
file, offset, orig);
goto out;
}
- if (entry->content == SNDRV_INFO_CONTENT_DATA)
- size = entry->size;
- else
- size = 0;
+
+ size = entry->size;
switch (orig) {
case SEEK_SET:
break;
@@ -201,45 +162,20 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
size_t count, loff_t * offset)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- struct snd_info_buffer *buf;
- size_t size = 0;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+ size_t size;
loff_t pos;
- data = file->private_data;
- if (snd_BUG_ON(!data))
- return -ENXIO;
pos = *offset;
- if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
- return -EIO;
- if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ if (!valid_pos(pos, count))
return -EIO;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- buf = data->rbuffer;
- if (buf == NULL)
- return -EIO;
- if (pos >= buf->size)
- return 0;
- size = buf->size - pos;
- size = min(count, size);
- if (copy_to_user(buffer, buf->buffer + pos, size))
- return -EFAULT;
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (pos >= entry->size)
- return 0;
- if (entry->c.ops->read) {
- size = entry->size - pos;
- size = min(count, size);
- size = entry->c.ops->read(entry,
- data->file_private_data,
- file, buffer, size, pos);
- }
- break;
- }
+ if (pos >= entry->size)
+ return 0;
+ size = entry->size - pos;
+ size = min(count, size);
+ size = entry->c.ops->read(entry, data->file_private_data,
+ file, buffer, size, pos);
if ((ssize_t) size > 0)
*offset = pos + size;
return size;
@@ -248,347 +184,319 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
size_t count, loff_t * offset)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- struct snd_info_buffer *buf;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
ssize_t size = 0;
loff_t pos;
- data = file->private_data;
- if (snd_BUG_ON(!data))
- return -ENXIO;
- entry = data->entry;
pos = *offset;
- if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
- return -EIO;
- if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ if (!valid_pos(pos, count))
return -EIO;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- buf = data->wbuffer;
- if (buf == NULL)
- return -EIO;
- mutex_lock(&entry->access);
- if (pos + count >= buf->len) {
- if (resize_info_buffer(buf, pos + count)) {
- mutex_unlock(&entry->access);
- return -ENOMEM;
- }
- }
- if (copy_from_user(buf->buffer + pos, buffer, count)) {
- mutex_unlock(&entry->access);
- return -EFAULT;
- }
- buf->size = pos + count;
- mutex_unlock(&entry->access);
- size = count;
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->write && count > 0) {
- size_t maxsize = entry->size - pos;
- count = min(count, maxsize);
- size = entry->c.ops->write(entry,
- data->file_private_data,
- file, buffer, count, pos);
- }
- break;
+ if (count > 0) {
+ size_t maxsize = entry->size - pos;
+ count = min(count, maxsize);
+ size = entry->c.ops->write(entry, data->file_private_data,
+ file, buffer, count, pos);
}
- if ((ssize_t) size > 0)
+ if (size > 0)
*offset = pos + size;
return size;
}
-static int snd_info_entry_open(struct inode *inode, struct file *file)
+static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait)
{
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+ unsigned int mask = 0;
+
+ if (entry->c.ops->poll)
+ return entry->c.ops->poll(entry,
+ data->file_private_data,
+ file, wait);
+ if (entry->c.ops->read)
+ mask |= POLLIN | POLLRDNORM;
+ if (entry->c.ops->write)
+ mask |= POLLOUT | POLLWRNORM;
+ return mask;
+}
+
+static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+
+ if (!entry->c.ops->ioctl)
+ return -ENOTTY;
+ return entry->c.ops->ioctl(entry, data->file_private_data,
+ file, cmd, arg);
+}
+
+static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct inode *inode = file_inode(file);
+ struct snd_info_private_data *data;
struct snd_info_entry *entry;
+
+ data = file->private_data;
+ if (data == NULL)
+ return 0;
+ entry = data->entry;
+ if (!entry->c.ops->mmap)
+ return -ENXIO;
+ return entry->c.ops->mmap(entry, data->file_private_data,
+ inode, file, vma);
+}
+
+static int snd_info_entry_open(struct inode *inode, struct file *file)
+{
+ struct snd_info_entry *entry = PDE_DATA(inode);
struct snd_info_private_data *data;
- struct snd_info_buffer *buffer;
int mode, err;
mutex_lock(&info_mutex);
- entry = PDE_DATA(inode);
- if (entry == NULL || ! entry->p) {
- mutex_unlock(&info_mutex);
- return -ENODEV;
- }
- if (!try_module_get(entry->module)) {
- err = -EFAULT;
- goto __error1;
- }
+ err = alloc_info_private(entry, &data);
+ if (err < 0)
+ goto unlock;
+
mode = file->f_flags & O_ACCMODE;
- if (mode == O_RDONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->read == NULL)) {
- err = -ENODEV;
- goto __error;
- }
+ if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
+ ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
+ err = -ENODEV;
+ goto error;
}
- if (mode == O_WRONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->write == NULL)) {
- err = -ENODEV;
- goto __error;
- }
- }
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (data == NULL) {
- err = -ENOMEM;
- goto __error;
- }
- data->entry = entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- if (mode == O_RDONLY || mode == O_RDWR) {
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL)
- goto __nomem;
- data->rbuffer = buffer;
- buffer->len = PAGE_SIZE;
- buffer->buffer = kzalloc(buffer->len, GFP_KERNEL);
- if (buffer->buffer == NULL)
- goto __nomem;
- }
- if (mode == O_WRONLY || mode == O_RDWR) {
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL)
- goto __nomem;
- data->wbuffer = buffer;
- buffer->len = PAGE_SIZE;
- buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
- if (buffer->buffer == NULL)
- goto __nomem;
- }
- break;
- case SNDRV_INFO_CONTENT_DATA: /* data */
- if (entry->c.ops->open) {
- if ((err = entry->c.ops->open(entry, mode,
- &data->file_private_data)) < 0) {
- kfree(data);
- goto __error;
- }
- }
- break;
+
+ if (entry->c.ops->open) {
+ err = entry->c.ops->open(entry, mode, &data->file_private_data);
+ if (err < 0)
+ goto error;
}
+
file->private_data = data;
mutex_unlock(&info_mutex);
- if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
- (mode == O_RDONLY || mode == O_RDWR)) {
- if (entry->c.text.read) {
- mutex_lock(&entry->access);
- entry->c.text.read(entry, data->rbuffer);
- mutex_unlock(&entry->access);
- }
- }
return 0;
- __nomem:
- if (data->rbuffer) {
- kfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- if (data->wbuffer) {
- kfree(data->wbuffer->buffer);
- kfree(data->wbuffer);
- }
+ error:
kfree(data);
- err = -ENOMEM;
- __error:
module_put(entry->module);
- __error1:
+ unlock:
mutex_unlock(&info_mutex);
return err;
}
static int snd_info_entry_release(struct inode *inode, struct file *file)
{
- struct snd_info_entry *entry;
- struct snd_info_private_data *data;
- int mode;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
- mode = file->f_flags & O_ACCMODE;
- data = file->private_data;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- if (data->rbuffer) {
- kfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- if (data->wbuffer) {
- if (entry->c.text.write) {
- entry->c.text.write(entry, data->wbuffer);
- if (data->wbuffer->error) {
- if (entry->card)
- dev_warn(entry->card->dev, "info: data write error to %s (%i)\n",
- entry->name,
- data->wbuffer->error);
- else
- pr_warn("ALSA: info: data write error to %s (%i)\n",
- entry->name,
- data->wbuffer->error);
- }
- }
- kfree(data->wbuffer->buffer);
- kfree(data->wbuffer);
- }
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->release)
- entry->c.ops->release(entry, mode,
- data->file_private_data);
- break;
- }
+ if (entry->c.ops->release)
+ entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
+ data->file_private_data);
module_put(entry->module);
kfree(data);
return 0;
}
-static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
+static const struct file_operations snd_info_entry_operations =
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- unsigned int mask;
+ .owner = THIS_MODULE,
+ .llseek = snd_info_entry_llseek,
+ .read = snd_info_entry_read,
+ .write = snd_info_entry_write,
+ .poll = snd_info_entry_poll,
+ .unlocked_ioctl = snd_info_entry_ioctl,
+ .mmap = snd_info_entry_mmap,
+ .open = snd_info_entry_open,
+ .release = snd_info_entry_release,
+};
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- mask = 0;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->poll)
- return entry->c.ops->poll(entry,
- data->file_private_data,
- file, wait);
- if (entry->c.ops->read)
- mask |= POLLIN | POLLRDNORM;
- if (entry->c.ops->write)
- mask |= POLLOUT | POLLWRNORM;
- break;
+/*
+ * file ops for text proc files
+ */
+static ssize_t snd_info_text_entry_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ struct seq_file *m = file->private_data;
+ struct snd_info_private_data *data = m->private;
+ struct snd_info_entry *entry = data->entry;
+ struct snd_info_buffer *buf;
+ loff_t pos;
+ size_t next;
+ int err = 0;
+
+ pos = *offset;
+ if (!valid_pos(pos, count))
+ return -EIO;
+ next = pos + count;
+ mutex_lock(&entry->access);
+ buf = data->wbuffer;
+ if (!buf) {
+ data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto error;
+ }
}
- return mask;
+ if (next > buf->len) {
+ char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!nbuf) {
+ err = -ENOMEM;
+ goto error;
+ }
+ buf->buffer = nbuf;
+ buf->len = PAGE_ALIGN(next);
+ }
+ if (copy_from_user(buf->buffer + pos, buffer, count)) {
+ err = -EFAULT;
+ goto error;
+ }
+ buf->size = next;
+ error:
+ mutex_unlock(&entry->access);
+ if (err < 0)
+ return err;
+ *offset = next;
+ return count;
}
-static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static int snd_info_seq_show(struct seq_file *seq, void *p)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
+ struct snd_info_private_data *data = seq->private;
+ struct snd_info_entry *entry = data->entry;
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->ioctl)
- return entry->c.ops->ioctl(entry,
- data->file_private_data,
- file, cmd, arg);
- break;
+ if (entry->c.text.read) {
+ data->rbuffer->buffer = (char *)seq; /* XXX hack! */
+ entry->c.text.read(entry, data->rbuffer);
}
- return -ENOTTY;
+ return 0;
}
-static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+static int snd_info_text_entry_open(struct inode *inode, struct file *file)
{
- struct inode *inode = file_inode(file);
+ struct snd_info_entry *entry = PDE_DATA(inode);
struct snd_info_private_data *data;
- struct snd_info_entry *entry;
+ int err;
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->mmap)
- return entry->c.ops->mmap(entry,
- data->file_private_data,
- inode, file, vma);
- break;
+ mutex_lock(&info_mutex);
+ err = alloc_info_private(entry, &data);
+ if (err < 0)
+ goto unlock;
+
+ data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
+ if (!data->rbuffer) {
+ err = -ENOMEM;
+ goto error;
}
- return -ENXIO;
+ if (entry->size)
+ err = single_open_size(file, snd_info_seq_show, data,
+ entry->size);
+ else
+ err = single_open(file, snd_info_seq_show, data);
+ if (err < 0)
+ goto error;
+ mutex_unlock(&info_mutex);
+ return 0;
+
+ error:
+ kfree(data->rbuffer);
+ kfree(data);
+ module_put(entry->module);
+ unlock:
+ mutex_unlock(&info_mutex);
+ return err;
}
-static const struct file_operations snd_info_entry_operations =
+static int snd_info_text_entry_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = file->private_data;
+ struct snd_info_private_data *data = m->private;
+ struct snd_info_entry *entry = data->entry;
+
+ if (data->wbuffer && entry->c.text.write)
+ entry->c.text.write(entry, data->wbuffer);
+
+ single_release(inode, file);
+ kfree(data->rbuffer);
+ if (data->wbuffer) {
+ kfree(data->wbuffer->buffer);
+ kfree(data->wbuffer);
+ }
+
+ module_put(entry->module);
+ kfree(data);
+ return 0;
+}
+
+static const struct file_operations snd_info_text_entry_ops =
{
.owner = THIS_MODULE,
- .llseek = snd_info_entry_llseek,
- .read = snd_info_entry_read,
- .write = snd_info_entry_write,
- .poll = snd_info_entry_poll,
- .unlocked_ioctl = snd_info_entry_ioctl,
- .mmap = snd_info_entry_mmap,
- .open = snd_info_entry_open,
- .release = snd_info_entry_release,
+ .open = snd_info_text_entry_open,
+ .release = snd_info_text_entry_release,
+ .write = snd_info_text_entry_write,
+ .llseek = seq_lseek,
+ .read = seq_read,
};
-int __init snd_info_init(void)
+static struct snd_info_entry *create_subdir(struct module *mod,
+ const char *name)
{
- struct proc_dir_entry *p;
+ struct snd_info_entry *entry;
- p = proc_mkdir("asound", NULL);
- if (p == NULL)
+ entry = snd_info_create_module_entry(mod, name, NULL);
+ if (!entry)
+ return NULL;
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return NULL;
+ }
+ return entry;
+}
+
+static struct snd_info_entry *
+snd_info_create_entry(const char *name, struct snd_info_entry *parent);
+
+int __init snd_info_init(void)
+{
+ snd_proc_root = snd_info_create_entry("asound", NULL);
+ if (!snd_proc_root)
return -ENOMEM;
- snd_proc_root = p;
+ snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ snd_proc_root->p = proc_mkdir("asound", NULL);
+ if (!snd_proc_root->p)
+ goto error;
#ifdef CONFIG_SND_OSSEMUL
- {
- struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_oss_root = entry;
- }
+ snd_oss_root = create_subdir(THIS_MODULE, "oss");
+ if (!snd_oss_root)
+ goto error;
#endif
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
- {
- struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_seq_root = entry;
- }
+ snd_seq_root = create_subdir(THIS_MODULE, "seq");
+ if (!snd_seq_root)
+ goto error;
#endif
- snd_info_version_init();
- snd_minor_info_init();
- snd_minor_info_oss_init();
- snd_card_info_init();
+ if (snd_info_version_init() < 0 ||
+ snd_minor_info_init() < 0 ||
+ snd_minor_info_oss_init() < 0 ||
+ snd_card_info_init() < 0 ||
+ snd_info_minor_register() < 0)
+ goto error;
return 0;
+
+ error:
+ snd_info_free_entry(snd_proc_root);
+ return -ENOMEM;
}
int __exit snd_info_done(void)
{
- snd_card_info_done();
- snd_minor_info_oss_done();
- snd_minor_info_done();
- snd_info_version_done();
- if (snd_proc_root) {
-#if IS_ENABLED(CONFIG_SND_SEQUENCER)
- snd_info_free_entry(snd_seq_root);
-#endif
-#ifdef CONFIG_SND_OSSEMUL
- snd_info_free_entry(snd_oss_root);
-#endif
- proc_remove(snd_proc_root);
- }
+ snd_info_free_entry(snd_proc_root);
return 0;
}
/*
-
- */
-
-
-/*
* create a card proc file
* called from init.c
*/
@@ -601,33 +509,58 @@ int snd_info_card_create(struct snd_card *card)
return -ENXIO;
sprintf(str, "card%i", card->number);
- if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = create_subdir(card->module, str);
+ if (!entry)
return -ENOMEM;
- }
card->proc_root = entry;
return 0;
}
+/* register all pending info entries */
+static int snd_info_register_recursive(struct snd_info_entry *entry)
+{
+ struct snd_info_entry *p;
+ int err;
+
+ if (!entry->p) {
+ err = snd_info_register(entry);
+ if (err < 0)
+ return err;
+ }
+
+ list_for_each_entry(p, &entry->children, list) {
+ err = snd_info_register_recursive(p);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
/*
* register the card proc file
* called from init.c
+ * can be called multiple times for reinitialization
*/
int snd_info_card_register(struct snd_card *card)
{
struct proc_dir_entry *p;
+ int err;
if (snd_BUG_ON(!card))
return -ENXIO;
+ err = snd_info_register_recursive(card->proc_root);
+ if (err < 0)
+ return err;
+
if (!strcmp(card->id, card->proc_root->name))
return 0;
- p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
- if (p == NULL)
+ if (card->proc_root_link)
+ return 0;
+ p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
+ if (!p)
return -ENOMEM;
card->proc_root_link = p;
return 0;
@@ -645,7 +578,7 @@ void snd_info_card_id_change(struct snd_card *card)
}
if (strcmp(card->id, card->proc_root->name))
card->proc_root_link = proc_symlink(card->id,
- snd_proc_root,
+ snd_proc_root->p,
card->proc_root->name);
mutex_unlock(&info_mutex);
}
@@ -753,9 +686,10 @@ const char *snd_info_get_str(char *dest, const char *src, int len)
EXPORT_SYMBOL(snd_info_get_str);
-/**
+/*
* snd_info_create_entry - create an info entry
* @name: the proc file name
+ * @parent: the parent directory
*
* Creates an info entry with the given file name and initializes as
* the default state.
@@ -765,7 +699,8 @@ EXPORT_SYMBOL(snd_info_get_str);
*
* Return: The pointer of the new instance, or %NULL on failure.
*/
-static struct snd_info_entry *snd_info_create_entry(const char *name)
+static struct snd_info_entry *
+snd_info_create_entry(const char *name, struct snd_info_entry *parent)
{
struct snd_info_entry *entry;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
@@ -781,6 +716,9 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
mutex_init(&entry->access);
INIT_LIST_HEAD(&entry->children);
INIT_LIST_HEAD(&entry->list);
+ entry->parent = parent;
+ if (parent)
+ list_add_tail(&entry->list, &parent->children);
return entry;
}
@@ -798,11 +736,9 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module,
const char *name,
struct snd_info_entry *parent)
{
- struct snd_info_entry *entry = snd_info_create_entry(name);
- if (entry) {
+ struct snd_info_entry *entry = snd_info_create_entry(name, parent);
+ if (entry)
entry->module = module;
- entry->parent = parent;
- }
return entry;
}
@@ -822,11 +758,10 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
struct snd_info_entry * parent)
{
- struct snd_info_entry *entry = snd_info_create_entry(name);
+ struct snd_info_entry *entry = snd_info_create_entry(name, parent);
if (entry) {
entry->module = card->module;
entry->card = card;
- entry->parent = parent;
}
return entry;
}
@@ -835,95 +770,39 @@ EXPORT_SYMBOL(snd_info_create_card_entry);
static void snd_info_disconnect(struct snd_info_entry *entry)
{
- struct list_head *p, *n;
- struct proc_dir_entry *root;
-
- list_for_each_safe(p, n, &entry->children) {
- snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
- }
+ struct snd_info_entry *p;
- if (! entry->p)
+ if (!entry->p)
return;
- list_del_init(&entry->list);
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- snd_BUG_ON(!root);
+ list_for_each_entry(p, &entry->children, list)
+ snd_info_disconnect(p);
proc_remove(entry->p);
entry->p = NULL;
}
-static int snd_info_dev_free_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- snd_info_free_entry(entry);
- return 0;
-}
-
-static int snd_info_dev_register_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- return snd_info_register(entry);
-}
-
-/**
- * snd_card_proc_new - create an info entry for the given card
- * @card: the card instance
- * @name: the file name
- * @entryp: the pointer to store the new info entry
- *
- * Creates a new info entry and assigns it to the given card.
- * Unlike snd_info_create_card_entry(), this function registers the
- * info entry as an ALSA device component, so that it can be
- * unregistered/released without explicit call.
- * Also, you don't have to register this entry via snd_info_register(),
- * since this will be registered by snd_card_register() automatically.
- *
- * The parent is assumed as card->proc_root.
- *
- * For releasing this entry, use snd_device_free() instead of
- * snd_info_free_entry().
- *
- * Return: Zero if successful, or a negative error code on failure.
- */
-int snd_card_proc_new(struct snd_card *card, const char *name,
- struct snd_info_entry **entryp)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_info_dev_free_entry,
- .dev_register = snd_info_dev_register_entry,
- /* disconnect is done via snd_info_card_disconnect() */
- };
- struct snd_info_entry *entry;
- int err;
-
- entry = snd_info_create_card_entry(card, name, card->proc_root);
- if (! entry)
- return -ENOMEM;
- if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
- snd_info_free_entry(entry);
- return err;
- }
- if (entryp)
- *entryp = entry;
- return 0;
-}
-
-EXPORT_SYMBOL(snd_card_proc_new);
-
/**
* snd_info_free_entry - release the info entry
* @entry: the info entry
*
- * Releases the info entry. Don't call this after registered.
+ * Releases the info entry.
*/
void snd_info_free_entry(struct snd_info_entry * entry)
{
- if (entry == NULL)
+ struct snd_info_entry *p, *n;
+
+ if (!entry)
return;
if (entry->p) {
mutex_lock(&info_mutex);
snd_info_disconnect(entry);
mutex_unlock(&info_mutex);
}
+
+ /* free all children at first */
+ list_for_each_entry_safe(p, n, &entry->children, list)
+ snd_info_free_entry(p);
+
+ list_del(&entry->list);
kfree(entry->name);
if (entry->private_free)
entry->private_free(entry);
@@ -946,7 +825,7 @@ int snd_info_register(struct snd_info_entry * entry)
if (snd_BUG_ON(!entry))
return -ENXIO;
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+ root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
mutex_lock(&info_mutex);
if (S_ISDIR(entry->mode)) {
p = proc_mkdir_mode(entry->name, entry->mode, root);
@@ -955,8 +834,13 @@ int snd_info_register(struct snd_info_entry * entry)
return -ENOMEM;
}
} else {
+ const struct file_operations *ops;
+ if (entry->content == SNDRV_INFO_CONTENT_DATA)
+ ops = &snd_info_entry_operations;
+ else
+ ops = &snd_info_text_entry_ops;
p = proc_create_data(entry->name, entry->mode, root,
- &snd_info_entry_operations, entry);
+ ops, entry);
if (!p) {
mutex_unlock(&info_mutex);
return -ENOMEM;
@@ -964,8 +848,6 @@ int snd_info_register(struct snd_info_entry * entry)
proc_set_size(p, entry->size);
}
entry->p = p;
- if (entry->parent)
- list_add_tail(&entry->list, &entry->parent->children);
mutex_unlock(&info_mutex);
return 0;
}
@@ -976,8 +858,6 @@ EXPORT_SYMBOL(snd_info_register);
*/
-static struct snd_info_entry *snd_info_version_entry;
-
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
snd_iprintf(buffer,
@@ -993,18 +873,5 @@ static int __init snd_info_version_init(void)
if (entry == NULL)
return -ENOMEM;
entry->c.text.read = snd_info_version_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_info_version_entry = entry;
- return 0;
+ return snd_info_register(entry); /* freed in error path */
}
-
-static int __exit snd_info_version_done(void)
-{
- snd_info_free_entry(snd_info_version_entry);
- return 0;
-}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index 83c29dbff9c0..1478c8dfd473 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -29,15 +29,12 @@
#include <linux/utsname.h>
#include <linux/mutex.h>
-#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
-
/*
* OSS compatible part
*/
static DEFINE_MUTEX(strings);
static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
-static struct snd_info_entry *snd_sndstat_proc_entry;
int snd_oss_info_register(int dev, int num, char *string)
{
@@ -112,27 +109,15 @@ static void snd_sndstat_proc_read(struct snd_info_entry *entry,
snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
}
-int snd_info_minor_register(void)
+int __init snd_info_minor_register(void)
{
struct snd_info_entry *entry;
memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
- entry->c.text.read = snd_sndstat_proc_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_sndstat_proc_entry = entry;
- return 0;
+ entry = snd_info_create_module_entry(THIS_MODULE, "sndstat",
+ snd_oss_root);
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_sndstat_proc_read;
+ return snd_info_register(entry); /* freed in error path */
}
-
-int snd_info_minor_unregister(void)
-{
- snd_info_free_entry(snd_sndstat_proc_entry);
- snd_sndstat_proc_entry = NULL;
- return 0;
-}
-
-#endif /* CONFIG_SND_OSSEMUL */
diff --git a/sound/core/init.c b/sound/core/init.c
index 04734e047bfe..3e0cebacefe1 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -100,35 +100,29 @@ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void snd_card_id_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
snd_iprintf(buffer, "%s\n", entry->card->id);
}
-static inline int init_info_for_card(struct snd_card *card)
+static int init_info_for_card(struct snd_card *card)
{
int err;
struct snd_info_entry *entry;
- if ((err = snd_info_card_register(card)) < 0) {
- dev_dbg(card->dev, "unable to create card info\n");
- return err;
- }
- if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
+ entry = snd_info_create_card_entry(card, "id", card->proc_root);
+ if (!entry) {
dev_dbg(card->dev, "unable to create card entry\n");
return err;
}
entry->c.text.read = snd_card_id_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
card->proc_id = entry;
- return 0;
+
+ return snd_info_card_register(card);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define init_info_for_card(card)
#endif
@@ -756,7 +750,7 @@ int snd_card_register(struct snd_card *card)
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
- return 0;
+ return snd_info_card_register(card); /* register pending info */
}
if (*card->id) {
/* make a unique id name from the given string */
@@ -782,9 +776,7 @@ int snd_card_register(struct snd_card *card)
EXPORT_SYMBOL(snd_card_register);
-#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *snd_card_info_entry;
-
+#ifdef CONFIG_SND_PROC_FS
static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -810,7 +802,6 @@ static void snd_card_info_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_OSSEMUL
-
void snd_card_info_read_oss(struct snd_info_buffer *buffer)
{
int idx, count;
@@ -832,7 +823,6 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
#endif
#ifdef MODULE
-static struct snd_info_entry *snd_card_module_info_entry;
static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -857,36 +847,21 @@ int __init snd_card_info_init(void)
if (! entry)
return -ENOMEM;
entry->c.text.read = snd_card_info_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_card_info_entry = entry;
+ if (snd_info_register(entry) < 0)
+ return -ENOMEM; /* freed in error path */
#ifdef MODULE
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
- if (entry) {
- entry->c.text.read = snd_card_module_info_read;
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
- else
- snd_card_module_info_entry = entry;
- }
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_card_module_info_read;
+ if (snd_info_register(entry) < 0)
+ return -ENOMEM; /* freed in error path */
#endif
return 0;
}
-
-int __exit snd_card_info_done(void)
-{
- snd_info_free_entry(snd_card_info_entry);
-#ifdef MODULE
- snd_info_free_entry(snd_card_module_info_entry);
-#endif
- return 0;
-}
-
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/**
* snd_component_add - add a component string
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 8658578eb584..7237acbdcbbc 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -24,6 +24,13 @@
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/core.h>
+#include <sound/control.h>
+
+struct snd_jack_kctl {
+ struct snd_kcontrol *kctl;
+ struct list_head list; /* list of controls belong to the same jack */
+ unsigned int mask_bits; /* only masked status bits are reported via kctl */
+};
static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_HEADPHONE_INSERT,
@@ -54,7 +61,13 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
static int snd_jack_dev_free(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
+ struct snd_card *card = device->card;
+ struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
+ list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
+ list_del_init(&jack_kctl->list);
+ snd_ctl_remove(card, jack_kctl->kctl);
+ }
if (jack->private_free)
jack->private_free(jack);
@@ -74,6 +87,10 @@ static int snd_jack_dev_register(struct snd_device *device)
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
+
+ if (!jack->input_dev)
+ return 0;
+
jack->input_dev->name = jack->name;
/* Default to the sound card device. */
@@ -100,6 +117,77 @@ static int snd_jack_dev_register(struct snd_device *device)
return err;
}
+static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ jack_kctl = kctl->private_data;
+ if (jack_kctl) {
+ list_del(&jack_kctl->list);
+ kfree(jack_kctl);
+ }
+}
+
+static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl)
+{
+ list_add_tail(&jack_kctl->list, &jack->kctl_list);
+}
+
+static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_jack_kctl *jack_kctl;
+ int err;
+
+ kctl = snd_kctl_jack_new(name, card);
+ if (!kctl)
+ return NULL;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return NULL;
+
+ jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL);
+
+ if (!jack_kctl)
+ goto error;
+
+ jack_kctl->kctl = kctl;
+ jack_kctl->mask_bits = mask;
+
+ kctl->private_data = jack_kctl;
+ kctl->private_free = snd_jack_kctl_private_free;
+
+ return jack_kctl;
+error:
+ snd_ctl_free_one(kctl);
+ return NULL;
+}
+
+/**
+ * snd_jack_add_new_kctl - Create a new snd_jack_kctl and add it to jack
+ * @jack: the jack instance which the kctl will attaching to
+ * @name: the name for the snd_kcontrol object
+ * @mask: a bitmask of enum snd_jack_type values that can be detected
+ * by this snd_jack_kctl object.
+ *
+ * Creates a new snd_kcontrol object and adds it to the jack kctl_list.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ jack_kctl = snd_jack_kctl_new(jack->card, name, mask);
+ if (!jack_kctl)
+ return -ENOMEM;
+
+ snd_jack_kctl_add(jack, jack_kctl);
+ return 0;
+}
+EXPORT_SYMBOL(snd_jack_add_new_kctl);
+
/**
* snd_jack_new - Create a new jack
* @card: the card instance
@@ -107,6 +195,8 @@ static int snd_jack_dev_register(struct snd_device *device)
* @type: a bitmask of enum snd_jack_type values that can be detected by
* this jack
* @jjack: Used to provide the allocated jack object to the caller.
+ * @initial_kctl: if true, create a kcontrol and add it to the jack list.
+ * @phantom_jack: Don't create a input device for phantom jacks.
*
* Creates a new jack object.
*
@@ -114,9 +204,10 @@ static int snd_jack_dev_register(struct snd_device *device)
* On success @jjack will be initialised.
*/
int snd_jack_new(struct snd_card *card, const char *id, int type,
- struct snd_jack **jjack)
+ struct snd_jack **jjack, bool initial_kctl, bool phantom_jack)
{
struct snd_jack *jack;
+ struct snd_jack_kctl *jack_kctl = NULL;
int err;
int i;
static struct snd_device_ops ops = {
@@ -125,31 +216,47 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
.dev_disconnect = snd_jack_dev_disconnect,
};
+ if (initial_kctl) {
+ jack_kctl = snd_jack_kctl_new(card, id, type);
+ if (!jack_kctl)
+ return -ENOMEM;
+ }
+
jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
if (jack == NULL)
return -ENOMEM;
jack->id = kstrdup(id, GFP_KERNEL);
- jack->input_dev = input_allocate_device();
- if (jack->input_dev == NULL) {
- err = -ENOMEM;
- goto fail_input;
- }
+ /* don't creat input device for phantom jack */
+ if (!phantom_jack) {
+ jack->input_dev = input_allocate_device();
+ if (jack->input_dev == NULL) {
+ err = -ENOMEM;
+ goto fail_input;
+ }
- jack->input_dev->phys = "ALSA";
+ jack->input_dev->phys = "ALSA";
- jack->type = type;
+ jack->type = type;
- for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
- if (type & (1 << i))
- input_set_capability(jack->input_dev, EV_SW,
- jack_switch_types[i]);
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
+ if (type & (1 << i))
+ input_set_capability(jack->input_dev, EV_SW,
+ jack_switch_types[i]);
+
+ }
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
goto fail_input;
+ jack->card = card;
+ INIT_LIST_HEAD(&jack->kctl_list);
+
+ if (initial_kctl)
+ snd_jack_kctl_add(jack, jack_kctl);
+
*jjack = jack;
return 0;
@@ -175,6 +282,8 @@ EXPORT_SYMBOL(snd_jack_new);
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
{
WARN_ON(jack->registered);
+ if (!jack->input_dev)
+ return;
jack->input_dev->dev.parent = parent;
}
@@ -230,11 +339,19 @@ EXPORT_SYMBOL(snd_jack_set_key);
*/
void snd_jack_report(struct snd_jack *jack, int status)
{
+ struct snd_jack_kctl *jack_kctl;
int i;
if (!jack)
return;
+ list_for_each_entry(jack_kctl, &jack->kctl_list, list)
+ snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+ status & jack_kctl->mask_bits);
+
+ if (!jack->input_dev)
+ return;
+
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
int testbit = SND_JACK_BTN_0 >> i;
@@ -252,9 +369,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
}
input_sync(jack->input_dev);
+
}
EXPORT_SYMBOL(snd_jack_report);
-
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_DESCRIPTION("Jack detection support for ALSA");
-MODULE_LICENSE("GPL");
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 056f8e274851..a99f7200ff3f 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1111,7 +1111,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
*/
#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
@@ -1255,10 +1255,10 @@ static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
snd_info_free_entry(mixer->proc_entry);
mixer->proc_entry = NULL;
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_mixer_oss_proc_init(mix)
#define snd_mixer_oss_proc_done(mix)
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index b25bcf5b8644..02bd96954dc4 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1027,7 +1027,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
static ssize_t show_pcm_class(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_pcm *pcm;
+ struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+ struct snd_pcm *pcm = pstr->pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
[SNDRV_PCM_CLASS_GENERIC] = "generic",
@@ -1036,8 +1037,7 @@ static ssize_t show_pcm_class(struct device *dev,
[SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
};
- if (! (pcm = dev_get_drvdata(dev)) ||
- pcm->dev_class > SNDRV_PCM_CLASS_LAST)
+ if (pcm->dev_class > SNDRV_PCM_CLASS_LAST)
str = "none";
else
str = strs[pcm->dev_class];
@@ -1181,7 +1181,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
}
EXPORT_SYMBOL(snd_pcm_notify);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -1227,10 +1227,10 @@ static void snd_pcm_proc_done(void)
snd_info_free_entry(snd_pcm_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_pcm_proc_init()
#define snd_pcm_proc_done()
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
new file mode 100644
index 000000000000..e70379fb63d0
--- /dev/null
+++ b/sound/core/pcm_drm_eld.c
@@ -0,0 +1,99 @@
+/*
+ * PCM DRM helpers
+ *
+ * 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/export.h>
+#include <drm/drm_edid.h>
+#include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
+
+static const unsigned int eld_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+static unsigned int sad_max_channels(const u8 *sad)
+{
+ return 1 + (sad[0] & 7);
+}
+
+static int eld_limit_rates(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r = hw_param_interval(params, rule->var);
+ struct snd_interval *c;
+ unsigned int rate_mask = 7, i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = drm_eld_sad(eld);
+ if (sad) {
+ c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
+ unsigned max_channels = sad_max_channels(sad);
+
+ /*
+ * Exclude SADs which do not include the
+ * requested number of channels.
+ */
+ if (c->min <= max_channels)
+ rate_mask |= sad[1];
+ }
+ }
+
+ return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
+ rate_mask);
+}
+
+static int eld_limit_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params, rule->var);
+ struct snd_interval *r;
+ struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
+ unsigned int i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = drm_eld_sad(eld);
+ if (sad) {
+ unsigned int rate_mask = 0;
+
+ /* Convert the rate interval to a mask */
+ r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
+ if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
+ rate_mask |= BIT(i);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
+ if (rate_mask & sad[1])
+ t.max = max(t.max, sad_max_channels(sad));
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
+{
+ int ret;
+
+ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ eld_limit_rates, eld,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ eld_limit_channels, eld,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c
new file mode 100644
index 000000000000..36b2d7aca1bd
--- /dev/null
+++ b/sound/core/pcm_iec958.c
@@ -0,0 +1,95 @@
+/*
+ * PCM DRM helpers
+ *
+ * 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/export.h>
+#include <linux/types.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+/**
+ * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
+ * @runtime: pcm runtime structure with ->rate filled in
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
+ size_t len)
+{
+ unsigned int fs, ws;
+
+ if (len < 4)
+ return -EINVAL;
+
+ switch (runtime->rate) {
+ case 32000:
+ fs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ fs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ fs = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ fs = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ fs = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ fs = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ fs = IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (len > 4) {
+ switch (snd_pcm_format_width(runtime->format)) {
+ case 16:
+ ws = IEC958_AES4_CON_WORDLEN_20_16;
+ break;
+ case 18:
+ ws = IEC958_AES4_CON_WORDLEN_22_18;
+ break;
+ case 20:
+ ws = IEC958_AES4_CON_WORDLEN_20_16 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case 24:
+ ws = IEC958_AES4_CON_WORDLEN_24_20 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ memset(cs, 0, len);
+
+ cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
+ cs[1] = IEC958_AES1_CON_GENERAL;
+ cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
+ cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
+
+ if (len > 4)
+ cs[4] = ws;
+
+ return len;
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 941f64a853eb..b65fa5a1943b 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -6,7 +6,8 @@
snd-seq-device-objs := seq_device.o
snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
- seq_system.o seq_ports.o seq_info.o
+ seq_system.o seq_ports.o
+snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
snd-seq-midi-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 72873a46afeb..7354b8bed860 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -45,7 +45,7 @@ MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
*/
static int register_device(void);
static void unregister_device(void);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static int register_proc(void);
static void unregister_proc(void);
#else
@@ -261,7 +261,7 @@ unregister_device(void)
* /proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
@@ -303,4 +303,4 @@ unregister_proc(void)
snd_info_free_entry(info_entry);
info_entry = NULL;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index 2de3feff70d0..b1221b29728e 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -479,8 +479,7 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
snd_seq_oss_timer_stop(dp->timer);
}
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* misc. functions for proc interface
*/
@@ -531,4 +530,4 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
snd_seq_oss_readq_info_read(dp->readq, buf);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index 96e8395ae586..aaff9ee32695 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -665,7 +665,7 @@ snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -705,4 +705,4 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
snd_use_lock_free(&mdev->use_lock);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index c080c73cea04..ccd893566f1d 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -222,7 +222,7 @@ snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -233,4 +233,4 @@ snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf
(waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
q->qlen, q->input_time);
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index 48e4fe1b68ab..0f3b38184fe5 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -630,7 +630,7 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -658,4 +658,4 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
snd_use_lock_free(&rec->use_lock);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index edbdab85fc02..b64f20deba90 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -2447,7 +2447,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
/*---------------------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* /proc interface
*/
@@ -2549,7 +2549,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
snd_seq_client_unlock(client);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*---------------------------------------------------------------------------*/
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index d99f99d61983..c4acf17e9f5e 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -72,7 +72,7 @@ static struct bus_type snd_seq_bus_type = {
/*
* proc interface -- just for compatibility
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
static int print_dev_info(struct device *dev, void *data)
@@ -272,7 +272,7 @@ EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
static int __init seq_dev_proc_init(void)
{
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
snd_seq_root);
if (info_entry == NULL)
@@ -305,7 +305,7 @@ static void __exit alsa_seq_device_exit(void)
#ifdef CONFIG_MODULES
cancel_work_sync(&autoload_work);
#endif
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_info_free_entry(info_entry);
#endif
bus_unregister(&snd_seq_bus_type);
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
index acf7769419f0..97015447b9b3 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -27,7 +27,6 @@
#include "seq_clientmgr.h"
#include "seq_timer.h"
-#ifdef CONFIG_PROC_FS
static struct snd_info_entry *queues_entry;
static struct snd_info_entry *clients_entry;
static struct snd_info_entry *timer_entry;
@@ -51,6 +50,13 @@ create_info_entry(char *name, void (*read)(struct snd_info_entry *,
return entry;
}
+static void free_info_entries(void)
+{
+ snd_info_free_entry(queues_entry);
+ snd_info_free_entry(clients_entry);
+ snd_info_free_entry(timer_entry);
+}
+
/* create all our /proc entries */
int __init snd_seq_info_init(void)
{
@@ -59,14 +65,17 @@ int __init snd_seq_info_init(void)
clients_entry = create_info_entry("clients",
snd_seq_info_clients_read);
timer_entry = create_info_entry("timer", snd_seq_info_timer_read);
+ if (!queues_entry || !clients_entry || !timer_entry)
+ goto error;
return 0;
+
+ error:
+ free_info_entries();
+ return -ENOMEM;
}
int __exit snd_seq_info_done(void)
{
- snd_info_free_entry(queues_entry);
- snd_info_free_entry(clients_entry);
- snd_info_free_entry(timer_entry);
+ free_info_entries();
return 0;
}
-#endif
diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h
index 4892a7f35c08..f8549f81a645 100644
--- a/sound/core/seq/seq_info.h
+++ b/sound/core/seq/seq_info.h
@@ -29,7 +29,7 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffe
void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int snd_seq_info_init( void );
int snd_seq_info_done( void );
#else
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index a0cda38205b9..7dfd0f429410 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -753,7 +753,7 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
/*----------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_queues_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -787,5 +787,5 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
queuefree(q);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 186f1611103c..82b220c769c1 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -422,7 +422,7 @@ snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_timer_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -449,5 +449,5 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry,
queuefree(q);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 5fc93d00572a..175f9e4e01c8 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -330,13 +330,10 @@ int snd_unregister_device(struct device *dev)
}
EXPORT_SYMBOL(snd_unregister_device);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* INFO PART
*/
-
-static struct snd_info_entry *snd_minor_info_entry;
-
static const char *snd_device_type_name(int type)
{
switch (type) {
@@ -389,23 +386,12 @@ int __init snd_minor_info_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
- if (entry) {
- entry->c.text.read = snd_minor_info_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_minor_info_entry = entry;
- return 0;
-}
-
-int __exit snd_minor_info_done(void)
-{
- snd_info_free_entry(snd_minor_info_entry);
- return 0;
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_minor_info_read;
+ return snd_info_register(entry); /* freed in error path */
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
* INIT PART
@@ -423,7 +409,6 @@ static int __init alsa_sound_init(void)
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
- snd_info_minor_register();
#ifndef MODULE
pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
@@ -432,7 +417,6 @@ static int __init alsa_sound_init(void)
static void __exit alsa_sound_exit(void)
{
- snd_info_minor_unregister();
snd_info_done();
unregister_chrdev(major, "alsa");
}
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 573a65eb2b79..0ca9d72b2273 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -19,12 +19,6 @@
*
*/
-#ifdef CONFIG_SND_OSSEMUL
-
-#if !IS_ENABLED(CONFIG_SOUND)
-#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
-#endif
-
#include <linux/init.h>
#include <linux/export.h>
#include <linux/slab.h>
@@ -213,10 +207,7 @@ EXPORT_SYMBOL(snd_unregister_oss_device);
* INFO PART
*/
-#ifdef CONFIG_PROC_FS
-
-static struct snd_info_entry *snd_minor_info_oss_entry;
-
+#ifdef CONFIG_SND_PROC_FS
static const char *snd_oss_device_type_name(int type)
{
switch (type) {
@@ -263,22 +254,9 @@ int __init snd_minor_info_oss_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
- if (entry) {
- entry->c.text.read = snd_minor_info_oss_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_minor_info_oss_entry = entry;
- return 0;
-}
-
-int __exit snd_minor_info_oss_done(void)
-{
- snd_info_free_entry(snd_minor_info_oss_entry);
- return 0;
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_minor_info_oss_read;
+ return snd_info_register(entry); /* freed in error path */
}
-#endif /* CONFIG_PROC_FS */
-
-#endif /* CONFIG_SND_OSSEMUL */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index a9a1a047c521..31f40f03e5b7 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1034,7 +1034,7 @@ static int snd_timer_register_system(void)
return snd_timer_global_register(timer);
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -1104,7 +1104,7 @@ static void __exit snd_timer_proc_done(void)
{
snd_info_free_entry(snd_timer_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_timer_proc_init()
#define snd_timer_proc_done()
#endif
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 7f9126efc1e5..54f348a4fb78 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1053,8 +1053,6 @@ static int loopback_mixer_new(struct loopback *loopback, int notify)
return 0;
}
-#ifdef CONFIG_PROC_FS
-
static void print_dpcm_info(struct snd_info_buffer *buffer,
struct loopback_pcm *dpcm,
const char *id)
@@ -1128,12 +1126,6 @@ static int loopback_proc_new(struct loopback *loopback, int cidx)
return 0;
}
-#else /* !CONFIG_PROC_FS */
-
-#define loopback_proc_new(loopback, cidx) do { } while (0)
-
-#endif
-
static int loopback_probe(struct platform_device *devptr)
{
struct snd_card *card;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index d11baaf0f0b4..016e451ed506 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -156,13 +156,13 @@ static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
return 0;
}
-struct dummy_model model_emu10k1 = {
+static struct dummy_model model_emu10k1 = {
.name = "emu10k1",
.playback_constraints = emu10k1_playback_constraints,
.buffer_bytes_max = 128 * 1024,
};
-struct dummy_model model_rme9652 = {
+static struct dummy_model model_rme9652 = {
.name = "rme9652",
.buffer_bytes_max = 26 * 64 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@@ -172,7 +172,7 @@ struct dummy_model model_rme9652 = {
.periods_max = 2,
};
-struct dummy_model model_ice1712 = {
+static struct dummy_model model_ice1712 = {
.name = "ice1712",
.buffer_bytes_max = 256 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@@ -182,7 +182,7 @@ struct dummy_model model_ice1712 = {
.periods_max = 1024,
};
-struct dummy_model model_uda1341 = {
+static struct dummy_model model_uda1341 = {
.name = "uda1341",
.buffer_bytes_max = 16380,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -192,7 +192,7 @@ struct dummy_model model_uda1341 = {
.periods_max = 255,
};
-struct dummy_model model_ac97 = {
+static struct dummy_model model_ac97 = {
.name = "ac97",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
@@ -202,7 +202,7 @@ struct dummy_model model_ac97 = {
.rate_max = 48000,
};
-struct dummy_model model_ca0106 = {
+static struct dummy_model model_ca0106 = {
.name = "ca0106",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.buffer_bytes_max = ((65536-64)*8),
@@ -216,7 +216,7 @@ struct dummy_model model_ca0106 = {
.rate_max = 192000,
};
-struct dummy_model *dummy_models[] = {
+static struct dummy_model *dummy_models[] = {
&model_emu10k1,
&model_rme9652,
&model_ice1712,
@@ -914,7 +914,7 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
return 0;
}
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS)
/*
* proc interface
*/
@@ -1042,7 +1042,7 @@ static void dummy_proc_init(struct snd_dummy *chip)
}
#else
#define dummy_proc_init(x)
-#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+#endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
static int snd_dummy_probe(struct platform_device *devptr)
{
diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile
index b94009b0b19f..c8eaa433d71a 100644
--- a/sound/drivers/opl4/Makefile
+++ b/sound/drivers/opl4/Makefile
@@ -3,7 +3,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
+snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o
+snd-opl4-lib-$(CONFIG_SND_PROC_FS) += opl4_proc.o
snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c
index 3b0ee42a5343..89c7aa04b3bc 100644
--- a/sound/drivers/opl4/opl4_lib.c
+++ b/sound/drivers/opl4/opl4_lib.c
@@ -176,9 +176,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
static void snd_opl4_free(struct snd_opl4 *opl4)
{
-#ifdef CONFIG_PROC_FS
snd_opl4_free_proc(opl4);
-#endif
release_and_free_resource(opl4->res_fm_port);
release_and_free_resource(opl4->res_pcm_port);
kfree(opl4);
@@ -249,9 +247,7 @@ int snd_opl4_create(struct snd_card *card,
snd_opl4_enable_opl4(opl4);
snd_opl4_create_mixer(opl4);
-#ifdef CONFIG_PROC_FS
snd_opl4_create_proc(opl4);
-#endif
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
opl4->seq_client = -1;
diff --git a/sound/drivers/opl4/opl4_local.h b/sound/drivers/opl4/opl4_local.h
index 470e5a758a02..9a41bdebce6b 100644
--- a/sound/drivers/opl4/opl4_local.h
+++ b/sound/drivers/opl4/opl4_local.h
@@ -178,7 +178,7 @@ struct snd_opl4 {
spinlock_t reg_lock;
struct snd_card *card;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
struct snd_info_entry *proc_entry;
int memory_access;
#endif
@@ -207,10 +207,13 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i
/* opl4_mixer.c */
int snd_opl4_create_mixer(struct snd_opl4 *opl4);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* opl4_proc.c */
int snd_opl4_create_proc(struct snd_opl4 *opl4);
void snd_opl4_free_proc(struct snd_opl4 *opl4);
+#else
+static inline int snd_opl4_create_proc(struct snd_opl4 *opl4) { return 0; }
+static inline void snd_opl4_free_proc(struct snd_opl4 *opl4) {}
#endif
/* opl4_seq.c */
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c
index 9b824bfc919d..cd2c07fa2ef4 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -22,8 +22,6 @@
#include <linux/export.h>
#include <sound/info.h>
-#ifdef CONFIG_PROC_FS
-
static int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
unsigned short mode, void **file_private_data)
{
@@ -129,5 +127,3 @@ void snd_opl4_free_proc(struct snd_opl4 *opl4)
{
snd_info_free_entry(opl4->proc_entry);
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index ecec547782b2..8850b7de1d38 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -95,6 +95,7 @@ config SND_BEBOB
* Tascam IF-FW/DM
* Behringer XENIX UFX 1204/1604
* Behringer Digital Mixer X32 series (X-UF Card)
+ * Behringer FCA610/1616
* Apogee Rosetta 200/400 (X-FireWire card)
* Apogee DA/AD/DD-16X (X-FireWire card)
* Apogee Ensemble
@@ -114,6 +115,7 @@ config SND_BEBOB
* M-Audio FireWire410/AudioPhile/Solo
* M-Audio Ozonic/NRV10/ProfireLightBridge
* M-Audio FireWire 1814/ProjectMix IO
+ * Digidesign Mbox 2 Pro
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index e061355f535f..7bb988fa6b6d 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -40,24 +40,28 @@
#define TAG_CIP 1
/* common isochronous packet header parameters */
-#define CIP_EOH (1u << 31)
+#define CIP_EOH_SHIFT 31
+#define CIP_EOH (1u << CIP_EOH_SHIFT)
#define CIP_EOH_MASK 0x80000000
-#define CIP_FMT_AM (0x10 << 24)
+#define CIP_SID_SHIFT 24
+#define CIP_SID_MASK 0x3f000000
+#define CIP_DBS_MASK 0x00ff0000
+#define CIP_DBS_SHIFT 16
+#define CIP_DBC_MASK 0x000000ff
+#define CIP_FMT_SHIFT 24
#define CIP_FMT_MASK 0x3f000000
+#define CIP_FDF_MASK 0x00ff0000
+#define CIP_FDF_SHIFT 16
#define CIP_SYT_MASK 0x0000ffff
#define CIP_SYT_NO_INFO 0xffff
-#define CIP_FDF_MASK 0x00ff0000
-#define CIP_FDF_SFC_SHIFT 16
/*
* Audio and Music transfer protocol specific parameters
* only "Clock-based rate control mode" is supported
*/
-#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3))
+#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT)
+#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3))
#define AMDTP_FDF_NO_DATA 0xff
-#define AMDTP_DBS_MASK 0x00ff0000
-#define AMDTP_DBS_SHIFT 16
-#define AMDTP_DBC_MASK 0x000000ff
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
@@ -251,19 +255,24 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
*/
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
- return 8 + s->syt_interval * s->data_block_quadlets * 4;
+ unsigned int multiplier = 1;
+
+ if (s->flags & CIP_JUMBO_PAYLOAD)
+ multiplier = 5;
+
+ return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-static void amdtp_write_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void amdtp_write_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void amdtp_read_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
/**
* amdtp_stream_set_pcm_format - set the PCM format
@@ -286,16 +295,16 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
/* fall through */
case SNDRV_PCM_FORMAT_S16:
if (s->direction == AMDTP_OUT_STREAM) {
- s->transfer_samples = amdtp_write_s16;
+ s->transfer_samples = write_pcm_s16;
break;
}
WARN_ON(1);
/* fall through */
case SNDRV_PCM_FORMAT_S32:
if (s->direction == AMDTP_OUT_STREAM)
- s->transfer_samples = amdtp_write_s32;
+ s->transfer_samples = write_pcm_s32;
else
- s->transfer_samples = amdtp_read_s32;
+ s->transfer_samples = read_pcm_s32;
break;
}
}
@@ -316,17 +325,25 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_stream *s)
+static unsigned int calculate_data_blocks(struct amdtp_stream *s,
+ unsigned int syt)
{
unsigned int phase, data_blocks;
- if (s->flags & CIP_BLOCKING)
- data_blocks = s->syt_interval;
- else if (!cip_sfc_is_base_44100(s->sfc)) {
- /* Sample_rate / 8000 is an integer, and precomputed. */
- data_blocks = s->data_block_state;
+ /* Blocking mode. */
+ if (s->flags & CIP_BLOCKING) {
+ /* This module generate empty packet for 'no data'. */
+ if (syt == CIP_SYT_NO_INFO)
+ data_blocks = 0;
+ else
+ data_blocks = s->syt_interval;
+ /* Non-blocking mode. */
} else {
- phase = s->data_block_state;
+ if (!cip_sfc_is_base_44100(s->sfc)) {
+ /* Sample_rate / 8000 is an integer, and precomputed. */
+ data_blocks = s->data_block_state;
+ } else {
+ phase = s->data_block_state;
/*
* This calculates the number of data blocks per packet so that
@@ -336,16 +353,17 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s)
* as possible in the sequence (to prevent underruns of the
* device's buffer).
*/
- if (s->sfc == CIP_SFC_44100)
- /* 6 6 5 6 5 6 5 ... */
- data_blocks = 5 + ((phase & 1) ^
- (phase == 0 || phase >= 40));
- else
- /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
- data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
- if (++phase >= (80 >> (s->sfc >> 1)))
- phase = 0;
- s->data_block_state = phase;
+ if (s->sfc == CIP_SFC_44100)
+ /* 6 6 5 6 5 6 5 ... */
+ data_blocks = 5 + ((phase & 1) ^
+ (phase == 0 || phase >= 40));
+ else
+ /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+ data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
+ if (++phase >= (80 >> (s->sfc >> 1)))
+ phase = 0;
+ s->data_block_state = phase;
+ }
}
return data_blocks;
@@ -394,9 +412,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
}
}
-static void amdtp_write_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
@@ -419,9 +437,9 @@ static void amdtp_write_s32(struct amdtp_stream *s,
}
}
-static void amdtp_write_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
@@ -444,9 +462,9 @@ static void amdtp_write_s16(struct amdtp_stream *s,
}
}
-static void amdtp_read_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
@@ -468,8 +486,8 @@ static void amdtp_read_s32(struct amdtp_stream *s,
}
}
-static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
unsigned int i, c;
@@ -510,8 +528,8 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
}
-static void amdtp_fill_midi(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+static void write_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
unsigned int f, port;
u8 *b;
@@ -537,8 +555,8 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
}
}
-static void amdtp_pull_midi(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
+static void read_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
unsigned int f, port;
int len;
@@ -633,57 +651,48 @@ static inline int queue_in_packet(struct amdtp_stream *s)
amdtp_stream_get_max_payload(s), false);
}
-static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
+static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
+ unsigned int syt)
{
__be32 *buffer;
- unsigned int data_blocks, payload_length;
+ unsigned int payload_length;
struct snd_pcm_substream *pcm;
- if (s->packet_index < 0)
- return;
-
- /* this module generate empty packet for 'no data' */
- if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO))
- data_blocks = calculate_data_blocks(s);
- else
- data_blocks = 0;
-
buffer = s->buffer.packets[s->packet_index].buffer;
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
- (s->data_block_quadlets << AMDTP_DBS_SHIFT) |
+ (s->data_block_quadlets << CIP_DBS_SHIFT) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
- (s->sfc << CIP_FDF_SFC_SHIFT) | syt);
+ (s->sfc << CIP_FDF_SHIFT) | syt);
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
s->transfer_samples(s, pcm, buffer, data_blocks);
else
- amdtp_fill_pcm_silence(s, buffer, data_blocks);
+ write_pcm_silence(s, buffer, data_blocks);
if (s->midi_ports)
- amdtp_fill_midi(s, buffer, data_blocks);
+ write_midi_messages(s, buffer, data_blocks);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
- if (queue_out_packet(s, payload_length, false) < 0) {
- s->packet_index = -1;
- amdtp_stream_pcm_abort(s);
- return;
- }
+ if (queue_out_packet(s, payload_length, false) < 0)
+ return -EIO;
if (pcm)
update_pcm_pointers(s, pcm, data_blocks);
+
+ /* No need to return the number of handled data blocks. */
+ return 0;
}
-static void handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets,
- __be32 *buffer)
+static int handle_in_packet(struct amdtp_stream *s,
+ unsigned int payload_quadlets, __be32 *buffer,
+ unsigned int *data_blocks)
{
u32 cip_header[2];
- unsigned int data_blocks, data_block_quadlets, data_block_counter,
- dbc_interval;
+ unsigned int data_block_quadlets, data_block_counter, dbc_interval;
struct snd_pcm_substream *pcm = NULL;
bool lost;
@@ -700,33 +709,34 @@ static void handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
+ *data_blocks = 0;
goto end;
}
/* Calculate data blocks */
if (payload_quadlets < 3 ||
((cip_header[1] & CIP_FDF_MASK) ==
- (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) {
- data_blocks = 0;
+ (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
+ *data_blocks = 0;
} else {
data_block_quadlets =
- (cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
+ (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
/* avoid division by zero */
if (data_block_quadlets == 0) {
- dev_info_ratelimited(&s->unit->device,
+ dev_err(&s->unit->device,
"Detect invalid value in dbs field: %08X\n",
cip_header[0]);
- goto err;
+ return -EPROTO;
}
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+ *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
}
/* Check data block counter continuity */
- data_block_counter = cip_header[0] & AMDTP_DBC_MASK;
- if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ data_block_counter = cip_header[0] & CIP_DBC_MASK;
+ if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
data_block_counter = s->data_block_counter;
@@ -736,49 +746,46 @@ static void handle_in_packet(struct amdtp_stream *s,
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter;
} else {
- if ((data_blocks > 0) && (s->tx_dbc_interval > 0))
+ if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
dbc_interval = s->tx_dbc_interval;
else
- dbc_interval = data_blocks;
+ dbc_interval = *data_blocks;
lost = data_block_counter !=
((s->data_block_counter + dbc_interval) & 0xff);
}
if (lost) {
- dev_info(&s->unit->device,
- "Detect discontinuity of CIP: %02X %02X\n",
- s->data_block_counter, data_block_counter);
- goto err;
+ dev_err(&s->unit->device,
+ "Detect discontinuity of CIP: %02X %02X\n",
+ s->data_block_counter, data_block_counter);
+ return -EIO;
}
- if (data_blocks > 0) {
+ if (*data_blocks > 0) {
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
- s->transfer_samples(s, pcm, buffer, data_blocks);
+ s->transfer_samples(s, pcm, buffer, *data_blocks);
if (s->midi_ports)
- amdtp_pull_midi(s, buffer, data_blocks);
+ read_midi_messages(s, buffer, *data_blocks);
}
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
else
s->data_block_counter =
- (data_block_counter + data_blocks) & 0xff;
+ (data_block_counter + *data_blocks) & 0xff;
end:
if (queue_in_packet(s) < 0)
- goto err;
+ return -EIO;
if (pcm)
- update_pcm_pointers(s, pcm, data_blocks);
+ update_pcm_pointers(s, pcm, *data_blocks);
- return;
-err:
- s->packet_index = -1;
- amdtp_stream_pcm_abort(s);
+ return 0;
}
static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
@@ -787,6 +794,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
{
struct amdtp_stream *s = private_data;
unsigned int i, syt, packets = header_length / 4;
+ unsigned int data_blocks;
+
+ if (s->packet_index < 0)
+ return;
/*
* Compute the cycle of the last queued packet.
@@ -797,8 +808,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
for (i = 0; i < packets; ++i) {
syt = calculate_syt(s, ++cycle);
- handle_out_packet(s, syt);
+ data_blocks = calculate_data_blocks(s, syt);
+
+ if (handle_out_packet(s, data_blocks, syt) < 0) {
+ s->packet_index = -1;
+ amdtp_stream_pcm_abort(s);
+ return;
+ }
}
+
fw_iso_context_queue_flush(s->context);
}
@@ -807,32 +825,55 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int p, syt, packets, payload_quadlets;
+ unsigned int p, syt, packets;
+ unsigned int payload_quadlets, max_payload_quadlets;
+ unsigned int data_blocks;
__be32 *buffer, *headers = header;
+ if (s->packet_index < 0)
+ return;
+
/* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE;
+ /* For buffer-over-run prevention. */
+ max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
+
for (p = 0; p < packets; p++) {
- if (s->packet_index < 0)
+ buffer = s->buffer.packets[s->packet_index].buffer;
+
+ /* The number of quadlets in this packet */
+ payload_quadlets =
+ (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+ if (payload_quadlets > max_payload_quadlets) {
+ dev_err(&s->unit->device,
+ "Detect jumbo payload: %02x %02x\n",
+ payload_quadlets, max_payload_quadlets);
+ s->packet_index = -1;
break;
+ }
- buffer = s->buffer.packets[s->packet_index].buffer;
+ if (handle_in_packet(s, payload_quadlets, buffer,
+ &data_blocks) < 0) {
+ s->packet_index = -1;
+ break;
+ }
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
- handle_out_packet(s->sync_slave, syt);
+ if (handle_out_packet(s->sync_slave,
+ data_blocks, syt) < 0) {
+ s->packet_index = -1;
+ break;
+ }
}
-
- /* The number of quadlets in this packet */
- payload_quadlets =
- (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
- handle_in_packet(s, payload_quadlets, buffer);
}
/* Queueing error or detecting discontinuity */
if (s->packet_index < 0) {
+ amdtp_stream_pcm_abort(s);
+
/* Abort sync slave. */
if (s->sync_slave) {
s->sync_slave->packet_index = -1;
@@ -872,7 +913,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM)
context->callback.sc = in_stream_callback;
- else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
+ else if (s->flags & CIP_SYNC_TO_DEVICE)
context->callback.sc = slave_stream_callback;
else
context->callback.sc = out_stream_callback;
@@ -1013,8 +1054,10 @@ EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
*/
void amdtp_stream_update(struct amdtp_stream *s)
{
+ /* Precomputing. */
ACCESS_ONCE(s->source_node_id_field) =
- (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
+ (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
+ CIP_SID_MASK;
}
EXPORT_SYMBOL(amdtp_stream_update);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 8a03a91e728b..26b909329e54 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -29,6 +29,9 @@
* packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct.
+ * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
+ * packet is larger than IEC 61883-6 defines. Current implementation
+ * allows 5 times as large as IEC 61883-6 defines.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -40,6 +43,7 @@ enum cip_flags {
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
CIP_SKIP_INIT_DBC_CHECK = 0x40,
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
+ CIP_JUMBO_PAYLOAD = 0x100,
};
/**
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 611b7dae7ee5..27a04ac8ffee 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -33,6 +33,7 @@ static DEFINE_MUTEX(devices_mutex);
static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
/* Offsets from information register. */
+#define INFO_OFFSET_BEBOB_VERSION 0x08
#define INFO_OFFSET_GUID 0x10
#define INFO_OFFSET_HW_MODEL_ID 0x18
#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
@@ -57,6 +58,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_FOCUSRITE 0x0000130e
#define VEN_MAUDIO1 0x00000d6c
#define VEN_MAUDIO2 0x000007f5
+#define VEN_DIGIDESIGN 0x00a07e
#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
@@ -72,6 +74,7 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
u32 hw_id;
u32 data[2] = {0};
u32 revision;
+ u32 version;
int err;
/* get vendor name from root directory */
@@ -104,6 +107,12 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
if (err < 0)
goto end;
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_BEBOB_VERSION,
+ &version);
+ if (err < 0)
+ goto end;
+ bebob->version = version;
+
strcpy(bebob->card->driver, "BeBoB");
strcpy(bebob->card->shortname, model);
strcpy(bebob->card->mixername, model);
@@ -364,6 +373,10 @@ static const struct ieee1394_device_id bebob_id_table[] = {
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
/* Behringer, Digital Mixer X32 series (X-UF Card) */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
+ /* Behringer, F-Control Audio 1616 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x001616, &spec_normal),
+ /* Behringer, F-Control Audio 610 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x000610, &spec_normal),
/* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
/* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
@@ -433,11 +446,11 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* M-Audio ProjectMix */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
&maudio_special_spec),
+ /* Digidesign Mbox 2 Pro */
+ SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal),
/* IDs are unknown but able to be supported */
/* Apogee, Mini-ME Firewire */
/* Apogee, Mini-DAC Firewire */
- /* Behringer, F-Control Audio 1616 */
- /* Behringer, F-Control Audio 610 */
/* Cakawalk, Sonar Power Studio 66 */
/* CME, UF400e */
/* ESI, Quotafire XL */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index dfbcd233178c..d23caca7f369 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -49,10 +49,15 @@ struct snd_bebob_stream_formation {
extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
/* device specific operations */
-#define SND_BEBOB_CLOCK_INTERNAL "Internal"
+enum snd_bebob_clock_type {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL = 0,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
+ SND_BEBOB_CLOCK_TYPE_SYT,
+};
struct snd_bebob_clock_spec {
unsigned int num;
const char *const *labels;
+ enum snd_bebob_clock_type *types;
int (*get)(struct snd_bebob *bebob, unsigned int *id);
};
struct snd_bebob_rate_spec {
@@ -92,8 +97,7 @@ struct snd_bebob {
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
- atomic_t capture_substreams;
- atomic_t playback_substreams;
+ atomic_t substreams_counter;
struct snd_bebob_stream_formation
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@@ -110,6 +114,9 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
bool deferred_registration;
+
+ /* For BeBoB version quirk. */
+ unsigned int version;
};
static inline int
@@ -159,7 +166,8 @@ enum avc_bridgeco_plug_type {
AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
- AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05
+ AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05,
+ AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06
};
static inline void
avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
@@ -205,8 +213,8 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
/* for AMDTP streaming */
int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
-int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
- bool *internal);
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src);
int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index fc67c1b7cb5b..a1a39494ea6c 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -103,11 +103,17 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
&data, sizeof(__be32), 0);
}
-static const char *const saffirepro_10_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+static enum snd_bebob_clock_type saffirepro_10_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
-static const char *const saffirepro_26_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
+static enum snd_bebob_clock_type saffirepro_26_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT1 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT2 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
/* Value maps between registers and labels for SaffirePro 10/26. */
static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = {
@@ -178,7 +184,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
goto end;
/* depending on hardware, use a different mapping */
- if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels)
+ if (bebob->spec->clock->types == saffirepro_10_clk_src_types)
map = saffirepro_clk_maps[0];
else
map = saffirepro_clk_maps[1];
@@ -195,8 +201,9 @@ end:
}
struct snd_bebob_spec saffire_le_spec;
-static const char *const saffire_both_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
+static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
};
static int
saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
@@ -259,8 +266,8 @@ static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
};
/* Saffire Pro 26 I/O */
static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
- .num = ARRAY_SIZE(saffirepro_26_clk_src_labels),
- .labels = saffirepro_26_clk_src_labels,
+ .num = ARRAY_SIZE(saffirepro_26_clk_src_types),
+ .types = saffirepro_26_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_26_spec = {
@@ -270,8 +277,8 @@ struct snd_bebob_spec saffirepro_26_spec = {
};
/* Saffire Pro 10 I/O */
static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
- .num = ARRAY_SIZE(saffirepro_10_clk_src_labels),
- .labels = saffirepro_10_clk_src_labels,
+ .num = ARRAY_SIZE(saffirepro_10_clk_src_types),
+ .types = saffirepro_10_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_10_spec = {
@@ -285,8 +292,8 @@ static struct snd_bebob_rate_spec saffire_both_rate_spec = {
.set = &snd_bebob_stream_set_rate,
};
static struct snd_bebob_clock_spec saffire_both_clk_spec = {
- .num = ARRAY_SIZE(saffire_both_clk_src_labels),
- .labels = saffire_both_clk_src_labels,
+ .num = ARRAY_SIZE(saffire_both_clk_src_types),
+ .types = saffire_both_clk_src_types,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index 9ee25a63f684..057495d54ab0 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -340,9 +340,12 @@ end:
}
/* Clock source control for special firmware */
-static const char *const special_clk_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
- "Word Clock", SND_BEBOB_CLOCK_INTERNAL};
+static enum snd_bebob_clock_type special_clk_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+};
static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
{
struct special_params *params = bebob->maudio_special_quirk;
@@ -352,7 +355,13 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
static int special_clk_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
- return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels),
+ static const char *const special_clk_labels[] = {
+ "Internal with Digital Mute",
+ "Digital",
+ "Word Clock",
+ "Internal"
+ };
+ return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),
special_clk_labels);
}
static int special_clk_ctl_get(struct snd_kcontrol *kctl,
@@ -371,7 +380,7 @@ static int special_clk_ctl_put(struct snd_kcontrol *kctl,
int err, id;
id = uval->value.enumerated.item[0];
- if (id >= ARRAY_SIZE(special_clk_labels))
+ if (id >= ARRAY_SIZE(special_clk_types))
return -EINVAL;
mutex_lock(&bebob->mutex);
@@ -708,8 +717,8 @@ static struct snd_bebob_rate_spec special_rate_spec = {
.set = &special_set_rate,
};
static struct snd_bebob_clock_spec special_clk_spec = {
- .num = ARRAY_SIZE(special_clk_labels),
- .labels = special_clk_labels,
+ .num = ARRAY_SIZE(special_clk_types),
+ .types = special_clk_types,
.get = &special_clk_get,
};
static struct snd_bebob_meter_spec special_meter_spec = {
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 63343d578df3..5681143925cd 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -17,7 +17,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&bebob->capture_substreams);
+ atomic_inc(&bebob->substreams_counter);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
@@ -34,7 +34,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&bebob->playback_substreams);
+ atomic_inc(&bebob->substreams_counter);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
@@ -46,7 +46,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
- atomic_dec(&bebob->capture_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
@@ -57,7 +57,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
- atomic_dec(&bebob->playback_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index 4a55561ed4ec..7a2c1f53bc44 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -157,7 +157,7 @@ pcm_open(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
struct snd_bebob_rate_spec *spec = bebob->spec->rate;
unsigned int sampling_rate;
- bool internal;
+ enum snd_bebob_clock_type src;
int err;
err = snd_bebob_stream_lock_try(bebob);
@@ -168,7 +168,7 @@ pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- err = snd_bebob_stream_check_internal_clock(bebob, &internal);
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
if (err < 0)
goto err_locked;
@@ -176,7 +176,7 @@ pcm_open(struct snd_pcm_substream *substream)
* When source of clock is internal or any PCM stream are running,
* the available sampling rate is limited at current sampling rate.
*/
- if (!internal ||
+ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
amdtp_stream_pcm_running(&bebob->tx_stream) ||
amdtp_stream_pcm_running(&bebob->rx_stream)) {
err = spec->get(bebob, &sampling_rate);
@@ -213,7 +213,7 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->capture_substreams);
+ 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,
@@ -226,7 +226,7 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->playback_substreams);
+ 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,
@@ -239,7 +239,7 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&bebob->capture_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
@@ -251,7 +251,7 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&bebob->playback_substreams);
+ atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
index 335da64506e0..301cc6a93945 100644
--- a/sound/firewire/bebob/bebob_proc.c
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -132,25 +132,27 @@ static void
proc_read_clock(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
+ static const char *const clk_labels[] = {
+ "Internal",
+ "External",
+ "SYT-Match",
+ };
struct snd_bebob *bebob = entry->private_data;
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
- unsigned int rate, id;
- bool internal;
+ enum snd_bebob_clock_type src;
+ unsigned int rate;
if (rate_spec->get(bebob, &rate) >= 0)
snd_iprintf(buffer, "Sampling rate: %d\n", rate);
- if (clk_spec) {
- if (clk_spec->get(bebob, &id) >= 0)
+ if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
+ if (clk_spec)
snd_iprintf(buffer, "Clock Source: %s\n",
- clk_spec->labels[id]);
- } else {
- if (snd_bebob_stream_check_internal_clock(bebob,
- &internal) >= 0)
+ clk_labels[src]);
+ else
snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
- (internal) ? "Internal" : "External",
- bebob->sync_input_plug);
+ clk_labels[src], bebob->sync_input_plug);
}
}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 98e4fc8121a1..5be5242e1ed8 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -8,7 +8,7 @@
#include "./bebob.h"
-#define CALLBACK_TIMEOUT 1000
+#define CALLBACK_TIMEOUT 2000
#define FW_ISO_RESOURCE_DELAY 1000
/*
@@ -116,16 +116,15 @@ end:
return err;
}
-int
-snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src)
{
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
unsigned int id;
+ enum avc_bridgeco_plug_type type;
int err = 0;
- *internal = false;
-
/* 1.The device has its own operation to switch source of clock */
if (clk_spec) {
err = clk_spec->get(bebob, &id);
@@ -143,10 +142,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
goto end;
}
- if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
- strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
- *internal = true;
-
+ *src = clk_spec->types[id];
goto end;
}
@@ -155,7 +151,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
* to use internal clock always
*/
if (bebob->sync_input_plug < 0) {
- *internal = true;
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
@@ -178,18 +174,79 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
* Here check the first field. This field is used for direction.
*/
if (input[0] == 0xff) {
- *internal = true;
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
- /*
- * If source of clock is internal CSR, Music Sub Unit Sync Input is
- * a destination of Music Sub Unit Sync Output.
- */
- *internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) &&
- (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) &&
- (input[2] == 0x0c) &&
- (input[3] == 0x00));
+ /* The source from any output plugs is for one purpose only. */
+ if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) {
+ /*
+ * In BeBoB architecture, the source from music subunit may
+ * bypass from oPCR[0]. This means that this source gives
+ * synchronization to IEEE 1394 cycle start packet.
+ */
+ if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT &&
+ input[2] == 0x0c) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ /* The source from any input units is for several purposes. */
+ } else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) {
+ if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) {
+ if (input[3] == 0x00) {
+ /*
+ * This source comes from iPCR[0]. This means
+ * that presentation timestamp calculated by
+ * SYT series of the received packets. In
+ * short, this driver is the master of
+ * synchronization.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_SYT;
+ goto end;
+ } else {
+ /*
+ * This source comes from iPCR[1-29]. This
+ * means that the synchronization stream is not
+ * the Audio/MIDI compound stream.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ }
+ } else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) {
+ /* Check type of this plug. */
+ avc_bridgeco_fill_unit_addr(addr,
+ AVC_BRIDGECO_PLUG_DIR_IN,
+ AVC_BRIDGECO_PLUG_UNIT_EXT,
+ input[3]);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr,
+ &type);
+ if (err < 0)
+ goto end;
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) {
+ /*
+ * SPDIF/ADAT or sometimes (not always) word
+ * clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ /* Often word clock. */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) {
+ /*
+ * Not standard.
+ * Mostly, additional internal clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ }
+ }
+
+ /* Not supported. */
+ err = -EIO;
end:
return err;
}
@@ -417,8 +474,24 @@ destroy_both_connections(struct snd_bebob *bebob)
static int
get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
{
- /* currently this module doesn't support SYT-Match mode */
- *sync_mode = CIP_SYNC_TO_DEVICE;
+ enum snd_bebob_clock_type src;
+ int err;
+
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
+ if (err < 0)
+ return err;
+
+ switch (src) {
+ case SND_BEBOB_CLOCK_TYPE_INTERNAL:
+ case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
+ *sync_mode = CIP_SYNC_TO_DEVICE;
+ break;
+ default:
+ case SND_BEBOB_CLOCK_TYPE_SYT:
+ *sync_mode = 0;
+ break;
+ }
+
return 0;
}
@@ -467,6 +540,17 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
/* See comments in next function */
init_completion(&bebob->bus_reset);
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+
+ /*
+ * BeBoB v3 transfers packets with these qurks:
+ * - In the beginning of streaming, the value of dbc is incremented
+ * even if no data blocks are transferred.
+ * - The value of dbc is reset suddenly.
+ */
+ if (bebob->version > 2)
+ bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
+ CIP_SKIP_DBC_ZERO_CHECK;
+
/*
* At high sampling rate, M-Audio special firmware transmits empty
* packet with the value of dbc incremented by 8 but the others are
@@ -490,7 +574,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct amdtp_stream *master, *slave;
- atomic_t *slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate;
bool updated = false;
@@ -515,8 +598,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
mutex_lock(&bebob->mutex);
/* Need no substreams */
- if (atomic_read(&bebob->playback_substreams) == 0 &&
- atomic_read(&bebob->capture_substreams) == 0)
+ if (atomic_read(&bebob->substreams_counter) == 0)
goto end;
err = get_sync_mode(bebob, &sync_mode);
@@ -525,11 +607,9 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &bebob->tx_stream;
slave = &bebob->rx_stream;
- slave_substreams = &bebob->playback_substreams;
} else {
master = &bebob->rx_stream;
slave = &bebob->tx_stream;
- slave_substreams = &bebob->capture_substreams;
}
/*
@@ -630,7 +710,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
}
/* start slave if needed */
- if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+ if (!amdtp_stream_running(slave)) {
err = start_stream(bebob, slave, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
@@ -656,31 +736,25 @@ end:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{
struct amdtp_stream *master, *slave;
- atomic_t *master_substreams, *slave_substreams;
if (bebob->master == &bebob->rx_stream) {
slave = &bebob->tx_stream;
master = &bebob->rx_stream;
- slave_substreams = &bebob->capture_substreams;
- master_substreams = &bebob->playback_substreams;
} else {
slave = &bebob->rx_stream;
master = &bebob->tx_stream;
- slave_substreams = &bebob->playback_substreams;
- master_substreams = &bebob->capture_substreams;
}
mutex_lock(&bebob->mutex);
- if (atomic_read(slave_substreams) == 0) {
+ if (atomic_read(&bebob->substreams_counter) == 0) {
+ amdtp_stream_pcm_abort(master);
+ amdtp_stream_stop(master);
+
amdtp_stream_pcm_abort(slave);
amdtp_stream_stop(slave);
- if (atomic_read(master_substreams) == 0) {
- amdtp_stream_pcm_abort(master);
- amdtp_stream_stop(master);
- break_both_connections(bebob);
- }
+ break_both_connections(bebob);
}
mutex_unlock(&bebob->mutex);
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index ad635004d699..9242e33d2cf1 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -8,8 +8,10 @@
#include "./bebob.h"
-static const char *const phase88_rack_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
+static enum snd_bebob_clock_type phase88_rack_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
static int
phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
@@ -34,13 +36,23 @@ end:
return err;
}
-static const char *const phase24_series_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "Digital In"
+static enum snd_bebob_clock_type phase24_series_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
};
static int
phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
- return avc_audio_get_selector(bebob->unit, 0, 4, id);
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 4, id);
+ if (err < 0)
+ return err;
+
+ if (*id >= ARRAY_SIZE(phase24_series_clk_src_types))
+ return -EIO;
+
+ return 0;
}
static struct snd_bebob_rate_spec phase_series_rate_spec = {
@@ -50,8 +62,8 @@ static struct snd_bebob_rate_spec phase_series_rate_spec = {
/* PHASE 88 Rack FW */
static struct snd_bebob_clock_spec phase88_rack_clk = {
- .num = ARRAY_SIZE(phase88_rack_clk_src_labels),
- .labels = phase88_rack_clk_src_labels,
+ .num = ARRAY_SIZE(phase88_rack_clk_src_types),
+ .types = phase88_rack_clk_src_types,
.get = &phase88_rack_clk_src_get,
};
struct snd_bebob_spec phase88_rack_spec = {
@@ -62,8 +74,8 @@ struct snd_bebob_spec phase88_rack_spec = {
/* 'PHASE 24 FW' and 'PHASE X24 FW' */
static struct snd_bebob_clock_spec phase24_series_clk = {
- .num = ARRAY_SIZE(phase24_series_clk_src_labels),
- .labels = phase24_series_clk_src_labels,
+ .num = ARRAY_SIZE(phase24_series_clk_src_types),
+ .types = phase24_series_clk_src_types,
.get = &phase24_series_clk_src_get,
};
struct snd_bebob_spec phase24_series_spec = {
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
index ef1fe3823a9c..58101702410b 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -28,15 +28,27 @@
* reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
*/
-static const char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
+static enum snd_bebob_clock_type clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+};
static int
clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
- return avc_audio_get_selector(bebob->unit, 0, 4, id);
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 4, id);
+ if (err < 0)
+ return err;
+
+ if (*id >= ARRAY_SIZE(clk_src_types))
+ return -EIO;
+
+ return 0;
}
static struct snd_bebob_clock_spec clock_spec = {
- .num = ARRAY_SIZE(clk_src_labels),
- .labels = clk_src_labels,
+ .num = ARRAY_SIZE(clk_src_types),
+ .types = clk_src_types,
.get = &clk_src_get,
};
static struct snd_bebob_rate_spec rate_spec = {
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index e6757cd85724..873d40fc4509 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -232,9 +232,15 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
goto end;
}
- /* OXFW starts to transmit packets with non-zero dbc. */
+ /*
+ * OXFW starts to transmit packets with non-zero dbc.
+ * OXFW postpone transferring packets till handling any asynchronous
+ * packets. As a result, next isochronous packet includes more data
+ * blocks than IEC 61883-6 defines.
+ */
if (stream == &oxfw->tx_stream)
- oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+ oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
+ CIP_JUMBO_PAYLOAD;
end:
return err;
}
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 001c6588a5ff..3129546398d0 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -1,3 +1,29 @@
config SND_HDA_CORE
tristate
select REGMAP
+
+config SND_HDA_DSP_LOADER
+ bool
+
+config SND_HDA_I915
+ bool
+ default y
+ depends on DRM_I915
+ depends on SND_HDA_CORE
+
+config SND_HDA_EXT_CORE
+ tristate
+ select SND_HDA_CORE
+
+config SND_HDA_PREALLOC_SIZE
+ int "Pre-allocated buffer size for HD-audio driver"
+ range 0 32768
+ default 64
+ help
+ Specifies the default pre-allocated buffer-size in kB for the
+ HD-audio driver. A larger buffer (e.g. 2048) is preferred
+ for systems using PulseAudio. The default 64 is chosen just
+ for compatibility reasons.
+
+ Note that the pre-allocation size can be changed dynamically
+ via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index 7a359f5b7e25..7e999c995cdc 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -1,7 +1,13 @@
snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
- hdac_regmap.o array.o
+ hdac_regmap.o hdac_controller.o hdac_stream.o array.o
snd-hda-core-objs += trace.o
CFLAGS_trace.o := -I$(src)
+# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o
+
obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
+
+#extended hda
+obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
diff --git a/sound/hda/ext/Makefile b/sound/hda/ext/Makefile
new file mode 100644
index 000000000000..9b6f641c7777
--- /dev/null
+++ b/sound/hda/ext/Makefile
@@ -0,0 +1,3 @@
+snd-hda-ext-core-objs := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o
+
+obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
new file mode 100644
index 000000000000..0aa5d9eb6c3f
--- /dev/null
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -0,0 +1,174 @@
+/*
+ * hdac-ext-bus.c - HD-audio extended core bus functions.
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+
+MODULE_DESCRIPTION("HDA extended core");
+MODULE_LICENSE("GPL v2");
+
+static void hdac_ext_writel(u32 value, u32 __iomem *addr)
+{
+ writel(value, addr);
+}
+
+static u32 hdac_ext_readl(u32 __iomem *addr)
+{
+ return readl(addr);
+}
+
+static void hdac_ext_writew(u16 value, u16 __iomem *addr)
+{
+ writew(value, addr);
+}
+
+static u16 hdac_ext_readw(u16 __iomem *addr)
+{
+ return readw(addr);
+}
+
+static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
+{
+ writeb(value, addr);
+}
+
+static u8 hdac_ext_readb(u8 __iomem *addr)
+{
+ return readb(addr);
+}
+
+static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type,
+ size_t size, struct snd_dma_buffer *buf)
+{
+ return snd_dma_alloc_pages(type, bus->dev, size, buf);
+}
+
+static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
+{
+ snd_dma_free_pages(buf);
+}
+
+static const struct hdac_io_ops hdac_ext_default_io = {
+ .reg_writel = hdac_ext_writel,
+ .reg_readl = hdac_ext_readl,
+ .reg_writew = hdac_ext_writew,
+ .reg_readw = hdac_ext_readw,
+ .reg_writeb = hdac_ext_writeb,
+ .reg_readb = hdac_ext_readb,
+ .dma_alloc_pages = hdac_ext_dma_alloc_pages,
+ .dma_free_pages = hdac_ext_dma_free_pages,
+};
+
+/**
+ * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
+ * @ebus: the pointer to extended bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
+ * default ops
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
+ const struct hdac_bus_ops *ops,
+ const struct hdac_io_ops *io_ops)
+{
+ int ret;
+ static int idx;
+
+ /* check if io ops are provided, if not load the defaults */
+ if (io_ops == NULL)
+ io_ops = &hdac_ext_default_io;
+
+ ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops);
+ if (ret < 0)
+ return ret;
+
+ INIT_LIST_HEAD(&ebus->hlink_list);
+ ebus->idx = idx++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
+
+/**
+ * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus
+ * @ebus: the pointer to extended bus object
+ */
+void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus)
+{
+ snd_hdac_bus_exit(&ebus->bus);
+ WARN_ON(!list_empty(&ebus->hlink_list));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
+
+static void default_release(struct device *dev)
+{
+ snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev));
+}
+
+/**
+ * snd_hdac_ext_device_init - initialize the HDA extended codec base device
+ * @ebus: hdac extended bus to attach to
+ * @addr: codec address
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
+{
+ 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)
+ return -ENOMEM;
+
+ snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);
+
+ ret = snd_hdac_device_init(hdev, bus, name, addr);
+ if (ret < 0) {
+ dev_err(bus->dev, "device init failed for hdac device\n");
+ return ret;
+ }
+ hdev->type = HDA_DEV_ASOC;
+ hdev->dev.release = default_release;
+
+ ret = snd_hdac_device_register(hdev);
+ if (ret) {
+ dev_err(bus->dev, "failed to register hdac device\n");
+ snd_hdac_ext_bus_device_exit(hdev);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init);
+
+/**
+ * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device
+ * @hdev: hdac device to clean up
+ */
+void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev)
+{
+ snd_hdac_device_exit(hdev);
+ kfree(hdev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit);
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
new file mode 100644
index 000000000000..b2da19b60f4e
--- /dev/null
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -0,0 +1,288 @@
+/*
+ * hdac-ext-controller.c - HD-audio extended controller functions.
+ *
+ * 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/delay.h>
+#include <linux/slab.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+
+/*
+ * maximum HDAC capablities we should parse to avoid endless looping:
+ * currently we have 4 extended caps, so this is future proof for now.
+ * extend when this limit is seen meeting in real HW
+ */
+#define HDAC_MAX_CAPS 10
+
+/**
+ * snd_hdac_ext_bus_parse_capabilities - parse capablity structure
+ * @ebus: the pointer to extended bus object
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus)
+{
+ unsigned int cur_cap;
+ unsigned int offset;
+ struct hdac_bus *bus = &ebus->bus;
+ unsigned int counter = 0;
+
+ offset = snd_hdac_chip_readl(bus, LLCH);
+
+ if (offset < 0)
+ return -EIO;
+
+ /* Lets walk the linked capabilities list */
+ do {
+ cur_cap = _snd_hdac_chip_read(l, bus, offset);
+
+ if (cur_cap < 0)
+ return -EIO;
+
+ dev_dbg(bus->dev, "Capability version: 0x%x\n",
+ ((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF));
+
+ dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
+
+ switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
+ case AZX_ML_CAP_ID:
+ dev_dbg(bus->dev, "Found ML capability\n");
+ ebus->mlcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_GTS_CAP_ID:
+ dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
+ ebus->gtscap = bus->remap_addr + offset;
+ break;
+
+ case AZX_PP_CAP_ID:
+ /* PP capability found, the Audio DSP is present */
+ dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
+ ebus->ppcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_SPB_CAP_ID:
+ /* SPIB capability found, handler function */
+ dev_dbg(bus->dev, "Found SPB capability\n");
+ ebus->spbcap = bus->remap_addr + offset;
+ break;
+
+ default:
+ dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
+ break;
+ }
+
+ counter++;
+
+ if (counter > HDAC_MAX_CAPS) {
+ dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n");
+ break;
+ }
+
+ /* read the offset of next capabiity */
+ offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
+
+ } while (offset);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities);
+
+/*
+ * processing pipe helpers - these helpers are useful for dealing with HDA
+ * new capability of processing pipelines
+ */
+
+/**
+ * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability
+ * @ebus: HD-audio extended core bus
+ * @enable: flag to turn on/off the capability
+ */
+void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
+{
+ struct hdac_bus *bus = &ebus->bus;
+
+ if (!ebus->ppcap) {
+ dev_err(bus->dev, "Address of PP capability is NULL");
+ return;
+ }
+
+ if (enable)
+ snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
+ else
+ snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
+
+/**
+ * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable
+ * @ebus: HD-audio extended core bus
+ * @enable: flag to enable/disable interrupt
+ */
+void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
+{
+ struct hdac_bus *bus = &ebus->bus;
+
+ if (!ebus->ppcap) {
+ dev_err(bus->dev, "Address of PP capability is NULL\n");
+ return;
+ }
+
+ if (enable)
+ snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
+ else
+ snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
+
+/*
+ * Multilink helpers - these helpers are useful for dealing with HDA
+ * new multilink capability
+ */
+
+/**
+ * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
+ * @ebus: HD-audio extended core bus
+ *
+ * This will parse all links and read the mlink capabilities and add them
+ * in hlink_list of extended hdac bus
+ * Note: this will be freed on bus exit by driver
+ */
+int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
+{
+ int idx;
+ u32 link_count;
+ struct hdac_ext_link *hlink;
+ struct hdac_bus *bus = &ebus->bus;
+
+ link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+ dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
+
+ for (idx = 0; idx < link_count; idx++) {
+ hlink = kzalloc(sizeof(*hlink), GFP_KERNEL);
+ if (!hlink)
+ return -ENOMEM;
+ hlink->index = idx;
+ 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);
+
+ list_add_tail(&hlink->list, &ebus->hlink_list);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
+
+/**
+ * snd_hdac_link_free_all- free hdac extended link objects
+ *
+ * @ebus: HD-audio ext core bus
+ */
+
+void snd_hdac_link_free_all(struct hdac_ext_bus *ebus)
+{
+ struct hdac_ext_link *l;
+
+ while (!list_empty(&ebus->hlink_list)) {
+ l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list);
+ list_del(&l->list);
+ kfree(l);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_link_free_all);
+
+/**
+ * snd_hdac_ext_bus_get_link_index - get link based on codec name
+ * @ebus: HD-audio extended core bus
+ * @codec_name: codec name
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus,
+ const char *codec_name)
+{
+ int i;
+ struct hdac_ext_link *hlink = NULL;
+ int bus_idx, addr;
+
+ if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
+ return NULL;
+ if (ebus->idx != bus_idx)
+ return NULL;
+
+ list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ for (i = 0; i < HDA_MAX_CODECS; i++) {
+ if (hlink->lsdiid & (0x1 << addr))
+ return hlink;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link);
+
+static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
+{
+ int timeout;
+ u32 val;
+ int mask = (1 << AZX_MLCTL_CPA);
+
+ udelay(3);
+ timeout = 50;
+
+ do {
+ val = snd_hdac_chip_readl(link->bus, ML_LCTL);
+ if (enable) {
+ if (((val & mask) >> AZX_MLCTL_CPA))
+ return 0;
+ } else {
+ if (!((val & mask) >> AZX_MLCTL_CPA))
+ return 0;
+ }
+ udelay(3);
+ } while (--timeout);
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_ext_bus_link_power_up -power up hda link
+ * @link: HD-audio extended link
+ */
+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);
+
+ return check_hdac_link_power_active(link, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
+
+/**
+ * snd_hdac_ext_bus_link_power_down -power down hda link
+ * @link: HD-audio extended link
+ */
+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);
+
+ return check_hdac_link_power_active(link, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
new file mode 100644
index 000000000000..f8ffbdbb450d
--- /dev/null
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -0,0 +1,452 @@
+/*
+ * hdac-ext-stream.c - HD-audio extended stream operations.
+ *
+ * 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+
+/**
+ * snd_hdac_ext_stream_init - initialize each stream (aka device)
+ * @ebus: HD-audio ext core bus
+ * @stream: HD-audio ext core stream object to initialize
+ * @idx: stream index number
+ * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
+ * @tag: the tag id to assign
+ *
+ * initialize the stream, if ppcap is enabled then init those and then
+ * invoke hdac stream initialization routine
+ */
+void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
+ struct hdac_ext_stream *stream,
+ int idx, int direction, int tag)
+{
+ struct hdac_bus *bus = &ebus->bus;
+
+ if (ebus->ppcap) {
+ stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE +
+ AZX_PPHC_INTERVAL * idx;
+
+ stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE +
+ AZX_PPLC_MULTI * ebus->num_streams +
+ AZX_PPLC_INTERVAL * idx;
+ }
+
+ stream->decoupled = false;
+ snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
+
+/**
+ * snd_hdac_ext_stream_init_all - create and initialize the stream objects
+ * for an extended hda bus
+ * @ebus: HD-audio ext core bus
+ * @start_idx: start index for streams
+ * @num_stream: number of streams to initialize
+ * @dir: direction of streams
+ */
+int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx,
+ int num_stream, int dir)
+{
+ int stream_tag = 0;
+ int i, tag, idx = start_idx;
+
+ for (i = 0; i < num_stream; i++) {
+ struct hdac_ext_stream *stream =
+ kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+ tag = ++stream_tag;
+ snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag);
+ idx++;
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
+
+/**
+ * snd_hdac_stream_free_all - free hdac extended stream objects
+ *
+ * @ebus: HD-audio ext core bus
+ */
+void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus)
+{
+ struct hdac_stream *s;
+ struct hdac_ext_stream *stream;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ while (!list_empty(&bus->stream_list)) {
+ s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
+ stream = stream_to_hdac_ext_stream(s);
+ list_del(&s->list);
+ kfree(stream);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
+
+/**
+ * snd_hdac_ext_stream_decouple - decouple the hdac stream
+ * @ebus: HD-audio ext core bus
+ * @stream: HD-audio ext core stream object to initialize
+ * @decouple: flag to decouple
+ */
+void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
+ struct hdac_ext_stream *stream, bool decouple)
+{
+ struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_bus *bus = &ebus->bus;
+
+ spin_lock_irq(&bus->reg_lock);
+ if (decouple)
+ snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0,
+ AZX_PPCTL_PROCEN(hstream->index));
+ else
+ snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PROCEN(hstream->index), 0);
+ stream->decoupled = decouple;
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
+
+/**
+ * snd_hdac_ext_linkstream_start - start a stream
+ * @stream: HD-audio ext core stream to start
+ */
+void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream)
+{
+ snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_RUN);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start);
+
+/**
+ * snd_hdac_ext_link_stream_clear - stop a stream DMA
+ * @stream: HD-audio ext core stream to stop
+ */
+void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream)
+{
+ snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear);
+
+/**
+ * snd_hdac_ext_link_stream_reset - reset a stream
+ * @stream: HD-audio ext core stream to reset
+ */
+void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream)
+{
+ unsigned char val;
+ int timeout;
+
+ snd_hdac_ext_link_stream_clear(stream);
+
+ snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_STRST);
+ udelay(3);
+ timeout = 50;
+ do {
+ val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) &
+ AZX_PPLCCTL_STRST;
+ if (val)
+ break;
+ udelay(3);
+ } while (--timeout);
+ val &= ~AZX_PPLCCTL_STRST;
+ writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
+ udelay(3);
+
+ timeout = 50;
+ /* waiting for hardware to report that the stream is out of reset */
+ do {
+ val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
+ if (!val)
+ break;
+ udelay(3);
+ } while (--timeout);
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset);
+
+/**
+ * snd_hdac_ext_link_stream_setup - set up the SD for streaming
+ * @stream: HD-audio ext core stream to set up
+ * @fmt: stream format
+ */
+int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt)
+{
+ struct hdac_stream *hstream = &stream->hstream;
+ unsigned int val;
+
+ /* make sure the run bit is zero for SD */
+ snd_hdac_ext_link_stream_clear(stream);
+ /* program the stream_tag */
+ val = readl(stream->pplc_addr + AZX_REG_PPLCCTL);
+ val = (val & ~AZX_PPLCCTL_STRM_MASK) |
+ (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
+ writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
+
+ /* program the stream format */
+ writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
+
+/**
+ * snd_hdac_ext_link_set_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
+ int stream)
+{
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
+
+/**
+ * snd_hdac_ext_link_clear_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
+ int stream)
+{
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, 0, (1 << stream));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
+
+static struct hdac_ext_stream *
+hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *stream = NULL;
+ struct hdac_bus *hbus = &ebus->bus;
+
+ if (!ebus->ppcap) {
+ dev_err(hbus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ list_for_each_entry(stream, &hbus->stream_list, list) {
+ struct hdac_ext_stream *hstream = container_of(stream,
+ struct hdac_ext_stream,
+ hstream);
+ if (stream->direction != substream->stream)
+ continue;
+
+ /* check if decoupled stream and not in use is available */
+ if (hstream->decoupled && !hstream->link_locked) {
+ res = hstream;
+ break;
+ }
+
+ if (!hstream->link_locked) {
+ snd_hdac_ext_stream_decouple(ebus, hstream, true);
+ res = hstream;
+ break;
+ }
+ }
+ if (res) {
+ spin_lock_irq(&hbus->reg_lock);
+ res->link_locked = 1;
+ res->link_substream = substream;
+ spin_unlock_irq(&hbus->reg_lock);
+ }
+ return res;
+}
+
+static struct hdac_ext_stream *
+hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
+ struct snd_pcm_substream *substream)
+{
+ 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,
+ hstream);
+ if (stream->direction != substream->stream)
+ continue;
+
+ if (stream->opened) {
+ if (!hstream->decoupled)
+ snd_hdac_ext_stream_decouple(ebus, hstream, true);
+ res = hstream;
+ break;
+ }
+ }
+ if (res) {
+ 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);
+ }
+
+ return res;
+}
+
+/**
+ * snd_hdac_ext_stream_assign - assign a stream for the PCM
+ * @ebus: HD-audio ext core bus
+ * @substream: PCM substream to assign
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * This assigns the stream based on the type (coupled/host/link), for the
+ * given PCM substream, assigns it and returns the stream object
+ *
+ * coupled: Looks for an unused stream
+ * host: Looks for an unused decoupled host stream
+ * link: Looks for an unused decoupled link stream
+ *
+ * If no stream is free, returns NULL. The function tries to keep using
+ * the same stream object when it's used beforehand. when a stream is
+ * decoupled, it becomes a host stream and link stream.
+ */
+struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus,
+ struct snd_pcm_substream *substream,
+ int type)
+{
+ struct hdac_ext_stream *hstream = NULL;
+ struct hdac_stream *stream = NULL;
+ struct hdac_bus *hbus = &ebus->bus;
+
+ switch (type) {
+ case HDAC_EXT_STREAM_TYPE_COUPLED:
+ stream = snd_hdac_stream_assign(hbus, substream);
+ if (stream)
+ hstream = container_of(stream,
+ struct hdac_ext_stream, hstream);
+ return hstream;
+
+ case HDAC_EXT_STREAM_TYPE_HOST:
+ return hdac_ext_host_stream_assign(ebus, substream);
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ return hdac_ext_link_stream_assign(ebus, substream);
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
+
+/**
+ * snd_hdac_ext_stream_release - release the assigned stream
+ * @stream: HD-audio ext core stream to release
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
+ */
+void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
+{
+ struct hdac_bus *bus = stream->hstream.bus;
+ struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
+
+ switch (type) {
+ case HDAC_EXT_STREAM_TYPE_COUPLED:
+ snd_hdac_stream_release(&stream->hstream);
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_HOST:
+ if (stream->decoupled) {
+ snd_hdac_ext_stream_decouple(ebus, stream, false);
+ snd_hdac_stream_release(&stream->hstream);
+ }
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ if (stream->decoupled)
+ snd_hdac_ext_stream_decouple(ebus, stream, false);
+ spin_lock_irq(&bus->reg_lock);
+ stream->link_locked = 0;
+ stream->link_substream = NULL;
+ spin_unlock_irq(&bus->reg_lock);
+ break;
+
+ default:
+ dev_dbg(bus->dev, "Invalid type %d\n", type);
+ }
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
+
+/**
+ * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream
+ * @ebus: HD-audio ext core bus
+ * @enable: flag to enable/disable SPIB
+ * @index: stream index for which SPIB need to be enabled
+ */
+void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
+ bool enable, int index)
+{
+ u32 mask = 0;
+ u32 register_mask = 0;
+ struct hdac_bus *bus = &ebus->bus;
+
+ if (!ebus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL");
+ return;
+ }
+
+ mask |= (1 << index);
+
+ register_mask = snd_hdac_chip_readl(bus, SPB_SPBFCCTL);
+
+ mask |= register_mask;
+
+ if (enable)
+ snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
+ else
+ snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
+
+/**
+ * snd_hdac_ext_stop_streams - stop all stream if running
+ * @ebus: HD-audio ext core bus
+ */
+void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_stream *stream;
+
+ if (bus->chip_init) {
+ list_for_each_entry(stream, &bus->stream_list, list)
+ snd_hdac_stream_stop(stream);
+ snd_hdac_bus_stop_chip(bus);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c
index 519914a12e8a..89c2711baaaf 100644
--- a/sound/hda/hda_bus_type.c
+++ b/sound/hda/hda_bus_type.c
@@ -10,6 +10,40 @@
MODULE_DESCRIPTION("HD-audio bus");
MODULE_LICENSE("GPL");
+/**
+ * hdac_get_device_id - gets the hdac device id entry
+ * @hdev: HD-audio core device
+ * @drv: HD-audio codec driver
+ *
+ * Compares the hdac device vendor_id and revision_id to the hdac_device
+ * driver id_table and returns the matching device id entry.
+ */
+const struct hda_device_id *
+hdac_get_device_id(struct hdac_device *hdev, struct hdac_driver *drv)
+{
+ if (drv->id_table) {
+ const struct hda_device_id *id = drv->id_table;
+
+ while (id->vendor_id) {
+ if (hdev->vendor_id == id->vendor_id &&
+ (!id->rev_id || id->rev_id == hdev->revision_id))
+ return id;
+ id++;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hdac_get_device_id);
+
+static int hdac_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
+{
+ if (hdac_get_device_id(dev, drv))
+ return 1;
+ else
+ return 0;
+}
+
static int hda_bus_match(struct device *dev, struct device_driver *drv)
{
struct hdac_device *hdev = dev_to_hdac_dev(dev);
@@ -17,8 +51,15 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
if (hdev->type != hdrv->type)
return 0;
+
+ /*
+ * if driver provided a match function use that otherwise we will
+ * use hdac_codec_match function
+ */
if (hdrv->match)
return hdrv->match(hdev, hdrv);
+ else
+ return hdac_codec_match(hdev, hdrv);
return 1;
}
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
index 8e262da74f6a..27c447e4fe5c 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -11,21 +11,36 @@
static void process_unsol_events(struct work_struct *work);
+static const struct hdac_bus_ops default_ops = {
+ .command = snd_hdac_bus_send_cmd,
+ .get_response = snd_hdac_bus_get_response,
+};
+
/**
* snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object
+ * @ops: bus verb operators
+ * @io_ops: lowlevel I/O operators
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
- const struct hdac_bus_ops *ops)
+ const struct hdac_bus_ops *ops,
+ const struct hdac_io_ops *io_ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
- bus->ops = ops;
+ if (ops)
+ bus->ops = ops;
+ else
+ bus->ops = &default_ops;
+ bus->io_ops = io_ops;
+ INIT_LIST_HEAD(&bus->stream_list);
INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, process_unsol_events);
+ spin_lock_init(&bus->reg_lock);
mutex_init(&bus->cmd_mutex);
+ bus->irq = -1;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
@@ -36,6 +51,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
*/
void snd_hdac_bus_exit(struct hdac_bus *bus)
{
+ WARN_ON(!list_empty(&bus->stream_list));
WARN_ON(!list_empty(&bus->codec_list));
cancel_work_sync(&bus->unsol_work);
}
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
new file mode 100644
index 000000000000..b5a17cb510a0
--- /dev/null
+++ b/sound/hda/hdac_controller.c
@@ -0,0 +1,507 @@
+/*
+ * HD-audio controller helpers
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+
+/* clear CORB read pointer properly */
+static void azx_clear_corbrp(struct hdac_bus *bus)
+{
+ int timeout;
+
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n",
+ snd_hdac_chip_readw(bus, CORBRP));
+
+ snd_hdac_chip_writew(bus, CORBRP, 0);
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (snd_hdac_chip_readw(bus, CORBRP) == 0)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n",
+ snd_hdac_chip_readw(bus, CORBRP));
+}
+
+/**
+ * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
+{
+ spin_lock_irq(&bus->reg_lock);
+ /* CORB set up */
+ bus->corb.addr = bus->rb.addr;
+ bus->corb.buf = (__le32 *)bus->rb.area;
+ snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr);
+ snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr));
+
+ /* set the corb size to 256 entries (ULI requires explicitly) */
+ snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ snd_hdac_chip_writew(bus, CORBWP, 0);
+
+ /* reset the corb hw read pointer */
+ snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
+ if (!bus->corbrp_self_clear)
+ azx_clear_corbrp(bus);
+
+ /* enable corb dma */
+ snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+
+ /* RIRB set up */
+ bus->rirb.addr = bus->rb.addr + 2048;
+ bus->rirb.buf = (__le32 *)(bus->rb.area + 2048);
+ bus->rirb.wp = bus->rirb.rp = 0;
+ memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds));
+ snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr);
+ snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr));
+
+ /* set the rirb size to 256 entries (ULI requires explicitly) */
+ snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
+ /* set N=1, get RIRB response interrupt for new entry */
+ snd_hdac_chip_writew(bus, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
+
+/**
+ * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
+{
+ spin_lock_irq(&bus->reg_lock);
+ /* disable ringbuffer DMAs */
+ snd_hdac_chip_writeb(bus, RIRBCTL, 0);
+ snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ /* disable unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io);
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (snd_BUG_ON(addr >= HDA_MAX_CODECS))
+ addr = 0;
+ return addr;
+}
+
+/**
+ * snd_hdac_bus_send_cmd - send a command verb via CORB
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+ unsigned int addr = azx_command_addr(val);
+ unsigned int wp, rp;
+
+ spin_lock_irq(&bus->reg_lock);
+
+ bus->last_cmd[azx_command_addr(val)] = val;
+
+ /* add command to corb */
+ wp = snd_hdac_chip_readw(bus, CORBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ spin_unlock_irq(&bus->reg_lock);
+ return -EIO;
+ }
+ wp++;
+ wp %= AZX_MAX_CORB_ENTRIES;
+
+ rp = snd_hdac_chip_readw(bus, CORBRP);
+ if (wp == rp) {
+ /* oops, it's full */
+ spin_unlock_irq(&bus->reg_lock);
+ return -EAGAIN;
+ }
+
+ bus->rirb.cmds[addr]++;
+ bus->corb.buf[wp] = cpu_to_le32(val);
+ snd_hdac_chip_writew(bus, CORBWP, wp);
+
+ spin_unlock_irq(&bus->reg_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
+
+#define AZX_RIRB_EX_UNSOL_EV (1<<4)
+
+/**
+ * snd_hdac_bus_update_rirb - retrieve RIRB entries
+ * @bus: HD-audio core bus
+ *
+ * Usually called from interrupt handler.
+ */
+void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
+{
+ unsigned int rp, wp;
+ unsigned int addr;
+ u32 res, res_ex;
+
+ wp = snd_hdac_chip_readw(bus, RIRBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ return;
+ }
+
+ if (wp == bus->rirb.wp)
+ return;
+ bus->rirb.wp = wp;
+
+ while (bus->rirb.rp != wp) {
+ bus->rirb.rp++;
+ bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
+
+ rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]);
+ res = le32_to_cpu(bus->rirb.buf[rp]);
+ addr = res_ex & 0xf;
+ if (addr >= HDA_MAX_CODECS) {
+ dev_err(bus->dev,
+ "spurious response %#x:%#x, rp = %d, wp = %d",
+ res, res_ex, bus->rirb.rp, wp);
+ snd_BUG();
+ } else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
+ snd_hdac_bus_queue_event(bus, res, res_ex);
+ else if (bus->rirb.cmds[addr]) {
+ bus->rirb.res[addr] = res;
+ bus->rirb.cmds[addr]--;
+ } else {
+ dev_err_ratelimited(bus->dev,
+ "spurious response %#x:%#x, last cmd=%#08x\n",
+ res, res_ex, bus->last_cmd[addr]);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
+
+/**
+ * snd_hdac_bus_get_response - receive a response via RIRB
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ unsigned long timeout;
+ unsigned long loopcounter;
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ for (loopcounter = 0;; loopcounter++) {
+ spin_lock_irq(&bus->reg_lock);
+ if (!bus->rirb.cmds[addr]) {
+ if (res)
+ *res = bus->rirb.res[addr]; /* the last value */
+ spin_unlock_irq(&bus->reg_lock);
+ return 0;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+ if (time_after(jiffies, timeout))
+ break;
+ if (loopcounter > 3000)
+ msleep(2); /* temporary workaround */
+ else {
+ udelay(10);
+ cond_resched();
+ }
+ }
+
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
+
+/*
+ * Lowlevel interface
+ */
+
+/**
+ * snd_hdac_bus_enter_link_reset - enter link reset
+ * @bus: HD-audio core bus
+ *
+ * Enter to the link reset state.
+ */
+void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ /* reset controller */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) &&
+ time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
+
+/**
+ * snd_hdac_bus_exit_link_reset - exit link reset
+ * @bus: HD-audio core bus
+ *
+ * Exit from the link reset state.
+ */
+void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
+
+/* reset codec link */
+static int azx_reset(struct hdac_bus *bus, bool full_reset)
+{
+ if (!full_reset)
+ goto skip_reset;
+
+ /* clear STATESTS */
+ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+ /* reset controller */
+ snd_hdac_bus_enter_link_reset(bus);
+
+ /* delay for >= 100us for codec PLL to settle per spec
+ * Rev 0.9 section 5.5.1
+ */
+ usleep_range(500, 1000);
+
+ /* Bring controller out of reset */
+ snd_hdac_bus_exit_link_reset(bus);
+
+ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
+ usleep_range(1000, 1200);
+
+ skip_reset:
+ /* check to see if controller is ready */
+ if (!snd_hdac_chip_readb(bus, GCTL)) {
+ dev_dbg(bus->dev, "azx_reset: controller not ready!\n");
+ return -EBUSY;
+ }
+
+ /* Accept unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL);
+
+ /* detect codecs */
+ if (!bus->codec_mask) {
+ bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ }
+
+ return 0;
+}
+
+/* enable interrupts */
+static void azx_int_enable(struct hdac_bus *bus)
+{
+ /* enable controller CIE and GIE */
+ snd_hdac_chip_updatel(bus, INTCTL, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(struct hdac_bus *bus)
+{
+ struct hdac_stream *azx_dev;
+
+ /* disable interrupts in stream descriptor */
+ list_for_each_entry(azx_dev, &bus->stream_list, list)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
+
+ /* disable SIE for all streams */
+ snd_hdac_chip_writeb(bus, INTCTL, 0);
+
+ /* disable controller CIE and GIE */
+ snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, 0);
+}
+
+/* clear interrupts */
+static void azx_int_clear(struct hdac_bus *bus)
+{
+ struct hdac_stream *azx_dev;
+
+ /* clear stream status */
+ list_for_each_entry(azx_dev, &bus->stream_list, list)
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+
+ /* clear STATESTS */
+ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+ /* clear rirb status */
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+
+ /* clear int status */
+ snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
+}
+
+/**
+ * snd_hdac_bus_init_chip - reset and start the controller registers
+ * @bus: HD-audio core bus
+ * @full_reset: Do full reset
+ */
+bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+ if (bus->chip_init)
+ return false;
+
+ /* reset controller */
+ azx_reset(bus, full_reset);
+
+ /* initialize interrupts */
+ azx_int_clear(bus);
+ azx_int_enable(bus);
+
+ /* initialize the codec command I/O */
+ snd_hdac_bus_init_cmd_io(bus);
+
+ /* program the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
+ snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
+ }
+
+ bus->chip_init = true;
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
+
+/**
+ * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_chip(struct hdac_bus *bus)
+{
+ if (!bus->chip_init)
+ return;
+
+ /* disable interrupts */
+ azx_int_disable(bus);
+ azx_int_clear(bus);
+
+ /* disable CORB/RIRB */
+ snd_hdac_bus_stop_cmd_io(bus);
+
+ /* disable position buffer */
+ if (bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, 0);
+ snd_hdac_chip_writel(bus, DPUBASE, 0);
+ }
+
+ bus->chip_init = false;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
+
+/**
+ * snd_hdac_bus_handle_stream_irq - interrupt handler for streams
+ * @bus: HD-audio core bus
+ * @status: INTSTS register value
+ * @ask: callback to be called for woken streams
+ */
+void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+ void (*ack)(struct hdac_bus *,
+ struct hdac_stream *))
+{
+ struct hdac_stream *azx_dev;
+ u8 sd_status;
+
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (status & azx_dev->sd_int_sta_mask) {
+ sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+ if (!azx_dev->substream || !azx_dev->running ||
+ !(sd_status & SD_INT_COMPLETE))
+ continue;
+ if (ack)
+ ack(bus, azx_dev);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
+
+/**
+ * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
+ * @bus: HD-audio core bus
+ *
+ * Call this after assigning the all streams.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
+{
+ struct hdac_stream *s;
+ int num_streams = 0;
+ int err;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ /* allocate memory for the BDL for each stream */
+ err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
+ BDL_SIZE, &s->bdl);
+ num_streams++;
+ if (err < 0)
+ return -ENOMEM;
+ }
+
+ if (WARN_ON(!num_streams))
+ return -EINVAL;
+ /* allocate memory for the position buffer */
+ err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
+ num_streams * 8, &bus->posbuf);
+ if (err < 0)
+ return -ENOMEM;
+ list_for_each_entry(s, &bus->stream_list, list)
+ s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
+
+ /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+ return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
+ PAGE_SIZE, &bus->rb);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
+
+/**
+ * snd_hdac_bus_free_stream_pages - release BDL and other buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
+{
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (s->bdl.area)
+ bus->io_ops->dma_free_pages(bus, &s->bdl);
+ }
+
+ if (bus->rb.area)
+ bus->io_ops->dma_free_pages(bus, &bus->rb);
+ if (bus->posbuf.area)
+ bus->io_ops->dma_free_pages(bus, &bus->posbuf);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index f75bf5622687..cdee7103f649 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -10,6 +10,7 @@
#include <linux/pm_runtime.h>
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
+#include <sound/pcm.h>
#include "local.h"
static void setup_fg_nodes(struct hdac_device *codec);
@@ -551,6 +552,21 @@ void snd_hdac_power_down_pm(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif
+/*
+ * Enable/disable the link power for a codec.
+ */
+int snd_hdac_link_power(struct hdac_device *codec, bool enable)
+{
+ if (!codec->link_power_control)
+ return 0;
+
+ if (codec->bus->ops->link_power)
+ return codec->bus->ops->link_power(codec->bus, enable);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_link_power);
+
/* codec vendor labels */
struct hda_vendor_id {
unsigned int id;
@@ -597,3 +613,302 @@ static int get_codec_vendor_name(struct hdac_device *codec)
codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
return codec->vendor_name ? 0 : -ENOMEM;
}
+
+/*
+ * stream formats
+ */
+struct hda_rate_tbl {
+ unsigned int hz;
+ unsigned int alsa_bits;
+ unsigned int hda_fmt;
+};
+
+/* rate = base * mult / div */
+#define HDA_RATE(base, mult, div) \
+ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
+ (((div) - 1) << AC_FMT_DIV_SHIFT))
+
+static struct hda_rate_tbl rate_bits[] = {
+ /* rate in Hz, ALSA rate bitmask, HDA format value */
+
+ /* autodetected value used in snd_hda_query_supported_pcm */
+ { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
+ { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
+ { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
+ { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
+ { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
+ { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
+ { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
+ { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
+ { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
+ { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
+ { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
+#define AC_PAR_PCM_RATE_BITS 11
+ /* up to bits 10, 384kHZ isn't supported properly */
+
+ /* not autodetected value */
+ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
+
+ { 0 } /* terminator */
+};
+
+/**
+ * snd_hdac_calc_stream_format - calculate the format bitset
+ * @rate: the sample rate
+ * @channels: the number of channels
+ * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
+ * @maxbps: the max. bps
+ * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
+ *
+ * Calculate the format bitset from the given rate, channels and th PCM format.
+ *
+ * Return zero if invalid.
+ */
+unsigned int snd_hdac_calc_stream_format(unsigned int rate,
+ unsigned int channels,
+ unsigned int format,
+ unsigned int maxbps,
+ unsigned short spdif_ctls)
+{
+ int i;
+ unsigned int val = 0;
+
+ for (i = 0; rate_bits[i].hz; i++)
+ if (rate_bits[i].hz == rate) {
+ val = rate_bits[i].hda_fmt;
+ break;
+ }
+ if (!rate_bits[i].hz)
+ return 0;
+
+ if (channels == 0 || channels > 8)
+ return 0;
+ val |= channels - 1;
+
+ switch (snd_pcm_format_width(format)) {
+ case 8:
+ val |= AC_FMT_BITS_8;
+ break;
+ case 16:
+ val |= AC_FMT_BITS_16;
+ break;
+ case 20:
+ case 24:
+ case 32:
+ if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
+ val |= AC_FMT_BITS_32;
+ else if (maxbps >= 24)
+ val |= AC_FMT_BITS_24;
+ else
+ val |= AC_FMT_BITS_20;
+ break;
+ default:
+ return 0;
+ }
+
+ if (spdif_ctls & AC_DIG1_NONAUDIO)
+ val |= AC_FMT_TYPE_NON_PCM;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format);
+
+static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int val = 0;
+
+ if (nid != codec->afg &&
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+ val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
+ if (!val || val == -1)
+ val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
+ if (!val || val == -1)
+ return 0;
+ return val;
+}
+
+static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
+
+ if (!streams || streams == -1)
+ streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
+ if (!streams || streams == -1)
+ return 0;
+ return streams;
+}
+
+/**
+ * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the codec object
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
+ * or @bsps argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
+ u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+{
+ unsigned int i, val, wcaps;
+
+ wcaps = get_wcaps(codec, nid);
+ val = query_pcm_param(codec, nid);
+
+ if (ratesp) {
+ u32 rates = 0;
+ for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
+ if (val & (1 << i))
+ rates |= rate_bits[i].alsa_bits;
+ }
+ if (rates == 0) {
+ dev_err(&codec->dev,
+ "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+ return -EIO;
+ }
+ *ratesp = rates;
+ }
+
+ if (formatsp || bpsp) {
+ u64 formats = 0;
+ unsigned int streams, bps;
+
+ streams = query_stream_param(codec, nid);
+ if (!streams)
+ return -EIO;
+
+ bps = 0;
+ if (streams & AC_SUPFMT_PCM) {
+ if (val & AC_SUPPCM_BITS_8) {
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (val & AC_SUPPCM_BITS_16) {
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+ bps = 16;
+ }
+ if (wcaps & AC_WCAP_DIGITAL) {
+ if (val & AC_SUPPCM_BITS_32)
+ formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (val & AC_SUPPCM_BITS_24)
+ bps = 24;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
+ } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
+ AC_SUPPCM_BITS_32)) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (val & AC_SUPPCM_BITS_32)
+ bps = 32;
+ else if (val & AC_SUPPCM_BITS_24)
+ bps = 24;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
+ }
+ }
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
+ if (streams & AC_SUPFMT_FLOAT32) {
+ formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+ if (!bps)
+ bps = 32;
+ }
+#endif
+ if (streams == AC_SUPFMT_AC3) {
+ /* should be exclusive */
+ /* temporary hack: we have still no proper support
+ * for the direct AC3 stream...
+ */
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (formats == 0) {
+ dev_err(&codec->dev,
+ "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+ streams);
+ return -EIO;
+ }
+ if (formatsp)
+ *formatsp = formats;
+ if (bpsp)
+ *bpsp = bps;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
+
+/**
+ * snd_hdac_is_supported_format - Check the validity of the format
+ * @codec: the codec object
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
+ *
+ * Returns true if supported, false if not.
+ */
+bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int format)
+{
+ int i;
+ unsigned int val = 0, rate, stream;
+
+ val = query_pcm_param(codec, nid);
+ if (!val)
+ return false;
+
+ rate = format & 0xff00;
+ for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
+ if (rate_bits[i].hda_fmt == rate) {
+ if (val & (1 << i))
+ break;
+ return false;
+ }
+ if (i >= AC_PAR_PCM_RATE_BITS)
+ return false;
+
+ stream = query_stream_param(codec, nid);
+ if (!stream)
+ return false;
+
+ if (stream & AC_SUPFMT_PCM) {
+ switch (format & 0xf0) {
+ case 0x00:
+ if (!(val & AC_SUPPCM_BITS_8))
+ return false;
+ break;
+ case 0x10:
+ if (!(val & AC_SUPPCM_BITS_16))
+ return false;
+ break;
+ case 0x20:
+ if (!(val & AC_SUPPCM_BITS_20))
+ return false;
+ break;
+ case 0x30:
+ if (!(val & AC_SUPPCM_BITS_24))
+ return false;
+ break;
+ case 0x40:
+ if (!(val & AC_SUPPCM_BITS_32))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ } else {
+ /* FIXME: check for float32 and AC3? */
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
new file mode 100644
index 000000000000..442500e06b7c
--- /dev/null
+++ b/sound/hda/hdac_i915.c
@@ -0,0 +1,196 @@
+/*
+ * hdac_i915.c - routines for sync between HD-A core and i915 display driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/component.h>
+#include <drm/i915_component.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+
+static struct i915_audio_component *hdac_acomp;
+
+int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+ struct i915_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ if (!acomp->ops->codec_wake_override) {
+ dev_warn(bus->dev,
+ "Invalid codec wake callback\n");
+ return 0;
+ }
+
+ dev_dbg(bus->dev, "%s codec wakeup\n",
+ enable ? "enable" : "disable");
+
+ acomp->ops->codec_wake_override(acomp->dev, enable);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
+
+int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
+{
+ struct i915_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ dev_dbg(bus->dev, "display power %s\n",
+ enable ? "enable" : "disable");
+
+ if (enable) {
+ if (!bus->i915_power_refcount++)
+ acomp->ops->get_power(acomp->dev);
+ } else {
+ WARN_ON(!bus->i915_power_refcount);
+ if (!--bus->i915_power_refcount)
+ acomp->ops->put_power(acomp->dev);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_display_power);
+
+int snd_hdac_get_display_clk(struct hdac_bus *bus)
+{
+ struct i915_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ return acomp->ops->get_cdclk_freq(acomp->dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
+
+static int hdac_component_master_bind(struct device *dev)
+{
+ struct i915_audio_component *acomp = hdac_acomp;
+ int ret;
+
+ ret = component_bind_all(dev, acomp);
+ if (ret < 0)
+ return ret;
+
+ if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
+ acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
+ ret = -EINVAL;
+ goto out_unbind;
+ }
+
+ /*
+ * Atm, we don't support dynamic unbinding initiated by the child
+ * component, so pin its containing module until we unbind.
+ */
+ if (!try_module_get(acomp->ops->owner)) {
+ ret = -ENODEV;
+ goto out_unbind;
+ }
+
+ return 0;
+
+out_unbind:
+ component_unbind_all(dev, acomp);
+
+ return ret;
+}
+
+static void hdac_component_master_unbind(struct device *dev)
+{
+ struct i915_audio_component *acomp = hdac_acomp;
+
+ module_put(acomp->ops->owner);
+ component_unbind_all(dev, acomp);
+ WARN_ON(acomp->ops || acomp->dev);
+}
+
+static const struct component_master_ops hdac_component_master_ops = {
+ .bind = hdac_component_master_bind,
+ .unbind = hdac_component_master_unbind,
+};
+
+static int hdac_component_master_match(struct device *dev, void *data)
+{
+ /* i915 is the only supported component */
+ return !strcmp(dev->driver->name, "i915");
+}
+
+int snd_hdac_i915_init(struct hdac_bus *bus)
+{
+ struct component_match *match = NULL;
+ struct device *dev = bus->dev;
+ struct i915_audio_component *acomp;
+ int ret;
+
+ acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
+ if (!acomp)
+ return -ENOMEM;
+ bus->audio_component = acomp;
+ hdac_acomp = acomp;
+
+ component_match_add(dev, &match, hdac_component_master_match, bus);
+ ret = component_master_add_with_match(dev, &hdac_component_master_ops,
+ match);
+ if (ret < 0)
+ goto out_err;
+
+ /*
+ * Atm, we don't support deferring the component binding, so make sure
+ * i915 is loaded and that the binding successfully completes.
+ */
+ request_module("i915");
+
+ if (!acomp->ops) {
+ ret = -ENODEV;
+ goto out_master_del;
+ }
+ dev_dbg(dev, "bound to i915 component master\n");
+
+ return 0;
+out_master_del:
+ component_master_del(dev, &hdac_component_master_ops);
+out_err:
+ kfree(acomp);
+ bus->audio_component = NULL;
+ dev_err(dev, "failed to add i915 component master (%d)\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
+
+int snd_hdac_i915_exit(struct hdac_bus *bus)
+{
+ struct device *dev = bus->dev;
+ struct i915_audio_component *acomp = bus->audio_component;
+
+ if (!acomp)
+ return 0;
+
+ WARN_ON(bus->i915_power_refcount);
+ if (bus->i915_power_refcount > 0 && acomp->ops)
+ acomp->ops->put_power(acomp->dev);
+
+ component_master_del(dev, &hdac_component_master_ops);
+
+ kfree(acomp);
+ bus->audio_component = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_exit);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
new file mode 100644
index 000000000000..4c15d0accc9e
--- /dev/null
+++ b/sound/hda/hdac_stream.c
@@ -0,0 +1,697 @@
+/*
+ * HD-audio stream operations
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "trace.h"
+
+/**
+ * snd_hdac_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @azx_dev: HD-audio core stream object to initialize
+ * @idx: stream index number
+ * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
+ * @tag: the tag id to assign
+ *
+ * Assign the starting bdl address to each stream (device) and initialize.
+ */
+void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
+ int idx, int direction, int tag)
+{
+ azx_dev->bus = bus;
+ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+ azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
+ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+ azx_dev->sd_int_sta_mask = 1 << idx;
+ azx_dev->index = idx;
+ azx_dev->direction = direction;
+ azx_dev->stream_tag = tag;
+ snd_hdac_dsp_lock_init(azx_dev);
+ list_add_tail(&azx_dev->list, &bus->stream_list);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
+
+/**
+ * snd_hdac_stream_start - start a stream
+ * @azx_dev: HD-audio core stream to start
+ * @fresh_start: false = wallclock timestamp relative to period wallclock
+ *
+ * Start a stream, set start_wallclk and set the running flag.
+ */
+void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ trace_snd_hdac_stream_start(bus, azx_dev);
+
+ azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
+ if (!fresh_start)
+ azx_dev->start_wallclk -= azx_dev->period_wallclk;
+
+ /* enable SIE */
+ snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index);
+ /* set DMA start and interrupt mask */
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ 0, SD_CTL_DMA_START | SD_INT_MASK);
+ azx_dev->running = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
+
+/**
+ * snd_hdac_stream_clear - stop a stream DMA
+ * @azx_dev: HD-audio core stream to stop
+ */
+void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
+{
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ SD_CTL_DMA_START | SD_INT_MASK, 0);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ azx_dev->running = false;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);
+
+/**
+ * snd_hdac_stream_stop - stop a stream
+ * @azx_dev: HD-audio core stream to stop
+ *
+ * Stop a stream DMA and disable stream interrupt
+ */
+void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
+{
+ trace_snd_hdac_stream_stop(azx_dev->bus, azx_dev);
+
+ snd_hdac_stream_clear(azx_dev);
+ /* disable SIE */
+ snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
+
+/**
+ * snd_hdac_stream_reset - reset a stream
+ * @azx_dev: HD-audio core stream to reset
+ */
+void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
+{
+ unsigned char val;
+ int timeout;
+
+ snd_hdac_stream_clear(azx_dev);
+
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+ udelay(3);
+ timeout = 300;
+ do {
+ val = snd_hdac_stream_readb(azx_dev, SD_CTL) &
+ SD_CTL_STREAM_RESET;
+ if (val)
+ break;
+ } while (--timeout);
+ val &= ~SD_CTL_STREAM_RESET;
+ snd_hdac_stream_writeb(azx_dev, SD_CTL, val);
+ udelay(3);
+
+ timeout = 300;
+ /* waiting for hardware to report that the stream is out of reset */
+ do {
+ val = snd_hdac_stream_readb(azx_dev, SD_CTL) &
+ SD_CTL_STREAM_RESET;
+ if (!val)
+ break;
+ } while (--timeout);
+
+ /* reset first position - may not be synced with hw at this time */
+ if (azx_dev->posbuf)
+ *azx_dev->posbuf = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_reset);
+
+/**
+ * snd_hdac_stream_setup - set up the SD for streaming
+ * @azx_dev: HD-audio core stream to set up
+ */
+int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_runtime *runtime;
+ unsigned int val;
+
+ if (azx_dev->substream)
+ runtime = azx_dev->substream->runtime;
+ else
+ runtime = NULL;
+ /* make sure the run bit is zero for SD */
+ snd_hdac_stream_clear(azx_dev);
+ /* program the stream_tag */
+ val = snd_hdac_stream_readl(azx_dev, SD_CTL);
+ val = (val & ~SD_CTL_STREAM_TAG_MASK) |
+ (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+ if (!bus->snoop)
+ val |= SD_CTL_TRAFFIC_PRIO;
+ snd_hdac_stream_writel(azx_dev, SD_CTL, val);
+
+ /* program the length of samples in cyclic buffer */
+ snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+ /* program the stream format */
+ /* this value needs to be the same as the one programmed */
+ snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+ /* program the stream LVI (last valid index) of the BDL */
+ snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+ /* program the BDL address */
+ /* lower BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
+ /* upper BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU,
+ upper_32_bits(azx_dev->bdl.addr));
+
+ /* enable the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE))
+ snd_hdac_chip_writel(bus, DPLBASE,
+ (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE);
+ }
+
+ /* set the interrupt enable bits in the descriptor control register */
+ snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
+
+ if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ azx_dev->fifo_size =
+ snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
+ else
+ azx_dev->fifo_size = 0;
+
+ /* when LPIB delay correction gives a small negative value,
+ * we ignore it; currently set the threshold statically to
+ * 64 frames
+ */
+ if (runtime && runtime->period_size > 64)
+ azx_dev->delay_negative_threshold =
+ -frames_to_bytes(runtime, 64);
+ else
+ azx_dev->delay_negative_threshold = 0;
+
+ /* wallclk has 24Mhz clock source */
+ if (runtime)
+ azx_dev->period_wallclk = (((runtime->period_size * 24000) /
+ runtime->rate) * 1000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup);
+
+/**
+ * snd_hdac_stream_cleanup - cleanup a stream
+ * @azx_dev: HD-audio core stream to clean up
+ */
+void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev)
+{
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+ snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup);
+
+/**
+ * snd_hdac_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ *
+ * Look for an unused stream for the given PCM substream, assign it
+ * and return the stream object. If no stream is free, returns NULL.
+ * The function tries to keep using the same stream object when it's used
+ * beforehand. Also, when bus->reverse_assign flag is set, the last free
+ * or matching entry is returned. This is needed for some strange codecs.
+ */
+struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *azx_dev;
+ struct hdac_stream *res = NULL;
+
+ /* make a non-zero unique key for the substream */
+ int key = (substream->pcm->device << 16) | (substream->number << 2) |
+ (substream->stream + 1);
+
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (azx_dev->direction != substream->stream)
+ continue;
+ if (azx_dev->opened)
+ continue;
+ if (azx_dev->assigned_key == key) {
+ res = azx_dev;
+ break;
+ }
+ if (!res || bus->reverse_assign)
+ res = azx_dev;
+ }
+ if (res) {
+ spin_lock_irq(&bus->reg_lock);
+ res->opened = 1;
+ res->running = 0;
+ res->assigned_key = key;
+ res->substream = substream;
+ spin_unlock_irq(&bus->reg_lock);
+ }
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
+
+/**
+ * snd_hdac_stream_release - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ */
+void snd_hdac_stream_release(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ spin_lock_irq(&bus->reg_lock);
+ azx_dev->opened = 0;
+ azx_dev->running = 0;
+ azx_dev->substream = NULL;
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct hdac_bus *bus,
+ struct snd_dma_buffer *dmab,
+ struct hdac_stream *azx_dev, __le32 **bdlp,
+ int ofs, int size, int with_ioc)
+{
+ __le32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_sgbuf_get_addr(dmab, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
+ /* one BDLE cannot cross 4K boundary on CTHDA chips */
+ if (bus->align_bdle_4k) {
+ u32 remain = 0x1000 - (ofs & 0xfff);
+
+ if (chunk > remain)
+ chunk = remain;
+ }
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ azx_dev->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/**
+ * snd_hdac_stream_setup_periods - set up BDL entries
+ * @azx_dev: HD-audio core stream to set up
+ *
+ * Set up the buffer descriptor table of the given stream based on the
+ * period and buffer sizes of the assigned PCM substream.
+ */
+int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ __le32 *bdl;
+ int i, ofs, periods, period_bytes;
+ int pos_adj, pos_align;
+
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+
+ period_bytes = azx_dev->period_bytes;
+ periods = azx_dev->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (__le32 *)azx_dev->bdl.area;
+ ofs = 0;
+ azx_dev->frags = 0;
+
+ pos_adj = bus->bdl_pos_adj;
+ if (!azx_dev->no_period_wakeup && pos_adj > 0) {
+ pos_align = pos_adj;
+ pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
+ if (!pos_adj)
+ pos_adj = pos_align;
+ else
+ pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
+ pos_align;
+ pos_adj = frames_to_bytes(runtime, pos_adj);
+ if (pos_adj >= period_bytes) {
+ dev_warn(bus->dev, "Too big adjustment %d\n",
+ pos_adj);
+ pos_adj = 0;
+ } else {
+ ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
+ azx_dev,
+ &bdl, ofs, pos_adj, true);
+ if (ofs < 0)
+ goto error;
+ }
+ } else
+ pos_adj = 0;
+
+ for (i = 0; i < periods; i++) {
+ if (i == periods - 1 && pos_adj)
+ ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
+ azx_dev, &bdl, ofs,
+ period_bytes - pos_adj, 0);
+ else
+ ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
+ azx_dev, &bdl, ofs,
+ period_bytes,
+ !azx_dev->no_period_wakeup);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ azx_dev->bufsize, period_bytes);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
+
+/* snd_hdac_stream_set_params - set stream parameters
+ * @azx_dev: HD-audio core stream for which parameters are to be set
+ * @format_val: format value parameter
+ *
+ * Setup the HD-audio core stream parameters from substream of the stream
+ * and passed format value
+ */
+int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
+ unsigned int format_val)
+{
+
+ unsigned int bufsize, period_bytes;
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ if (!substream)
+ return -EINVAL;
+ runtime = substream->runtime;
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+
+ if (bufsize != azx_dev->bufsize ||
+ period_bytes != azx_dev->period_bytes ||
+ format_val != azx_dev->format_val ||
+ runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
+ azx_dev->bufsize = bufsize;
+ azx_dev->period_bytes = period_bytes;
+ azx_dev->format_val = format_val;
+ azx_dev->no_period_wakeup = runtime->no_period_wakeup;
+ err = snd_hdac_stream_setup_periods(azx_dev);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params);
+
+static cycle_t azx_cc_read(const struct cyclecounter *cc)
+{
+ struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc);
+
+ return snd_hdac_chip_readl(azx_dev->bus, WALLCLK);
+}
+
+static void azx_timecounter_init(struct hdac_stream *azx_dev,
+ bool force, cycle_t last)
+{
+ struct timecounter *tc = &azx_dev->tc;
+ struct cyclecounter *cc = &azx_dev->cc;
+ u64 nsec;
+
+ cc->read = azx_cc_read;
+ cc->mask = CLOCKSOURCE_MASK(32);
+
+ /*
+ * Converting from 24 MHz to ns means applying a 125/3 factor.
+ * To avoid any saturation issues in intermediate operations,
+ * the 125 factor is applied first. The division is applied
+ * last after reading the timecounter value.
+ * Applying the 1/3 factor as part of the multiplication
+ * requires at least 20 bits for a decent precision, however
+ * overflows occur after about 4 hours or less, not a option.
+ */
+
+ cc->mult = 125; /* saturation after 195 years */
+ cc->shift = 0;
+
+ nsec = 0; /* audio time is elapsed time since trigger */
+ timecounter_init(tc, cc, nsec);
+ if (force) {
+ /*
+ * force timecounter to use predefined value,
+ * used for synchronized starts
+ */
+ tc->cycle_last = last;
+ }
+}
+
+/**
+ * snd_hdac_stream_timecounter_init - initialize time counter
+ * @azx_dev: HD-audio core stream (master stream)
+ * @streams: bit flags of streams to set up
+ *
+ * Initializes the time counter of streams marked by the bit flags (each
+ * bit corresponds to the stream index).
+ * The trigger timestamp of PCM substream assigned to the given stream is
+ * updated accordingly, too.
+ */
+void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
+ unsigned int streams)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_runtime *runtime = azx_dev->substream->runtime;
+ struct hdac_stream *s;
+ bool inited = false;
+ cycle_t cycle_last = 0;
+ int i = 0;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (streams & (1 << i)) {
+ azx_timecounter_init(s, inited, cycle_last);
+ if (!inited) {
+ inited = true;
+ cycle_last = s->tc.cycle_last;
+ }
+ }
+ i++;
+ }
+
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ runtime->trigger_tstamp_latched = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init);
+
+/**
+ * snd_hdac_stream_sync_trigger - turn on/off stream sync register
+ * @azx_dev: HD-audio core stream (master stream)
+ * @streams: bit flags of streams to sync
+ */
+void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
+ unsigned int streams, unsigned int reg)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ unsigned int val;
+
+ if (!reg)
+ reg = AZX_REG_SSYNC;
+ val = _snd_hdac_chip_read(l, bus, reg);
+ if (set)
+ val |= streams;
+ else
+ val &= ~streams;
+ _snd_hdac_chip_write(l, bus, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
+
+/**
+ * snd_hdac_stream_sync - sync with start/strop trigger operation
+ * @azx_dev: HD-audio core stream (master stream)
+ * @start: true = start, false = stop
+ * @streams: bit flags of streams to sync
+ *
+ * For @start = true, wait until all FIFOs get ready.
+ * For @start = false, wait until all RUN bits are cleared.
+ */
+void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
+ unsigned int streams)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int i, nwait, timeout;
+ struct hdac_stream *s;
+
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ i = 0;
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (streams & (1 << i)) {
+ if (start) {
+ /* check FIFO gets ready */
+ if (!(snd_hdac_stream_readb(s, SD_STS) &
+ SD_STS_FIFO_READY))
+ nwait++;
+ } else {
+ /* check RUN bit is cleared */
+ if (snd_hdac_stream_readb(s, SD_CTL) &
+ SD_CTL_DMA_START)
+ nwait++;
+ }
+ }
+ i++;
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/**
+ * snd_hdac_dsp_prepare - prepare for DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @format: HD-audio stream format
+ * @byte_size: data chunk byte size
+ * @bufp: allocated buffer
+ *
+ * Allocate the buffer for the given size and set up the given stream for
+ * DSP loading. Returns the stream tag (>= 0), or a negative error code.
+ */
+int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
+ unsigned int byte_size, struct snd_dma_buffer *bufp)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ u32 *bdl;
+ int err;
+
+ snd_hdac_dsp_lock(azx_dev);
+ spin_lock_irq(&bus->reg_lock);
+ if (azx_dev->running || azx_dev->locked) {
+ spin_unlock_irq(&bus->reg_lock);
+ err = -EBUSY;
+ goto unlock;
+ }
+ azx_dev->locked = true;
+ spin_unlock_irq(&bus->reg_lock);
+
+ err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG,
+ byte_size, bufp);
+ if (err < 0)
+ goto err_alloc;
+
+ azx_dev->substream = NULL;
+ azx_dev->bufsize = byte_size;
+ azx_dev->period_bytes = byte_size;
+ azx_dev->format_val = format;
+
+ snd_hdac_stream_reset(azx_dev);
+
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+
+ azx_dev->frags = 0;
+ bdl = (u32 *)azx_dev->bdl.area;
+ err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0);
+ if (err < 0)
+ goto error;
+
+ snd_hdac_stream_setup(azx_dev);
+ snd_hdac_dsp_unlock(azx_dev);
+ return azx_dev->stream_tag;
+
+ error:
+ bus->io_ops->dma_free_pages(bus, bufp);
+ err_alloc:
+ spin_lock_irq(&bus->reg_lock);
+ azx_dev->locked = false;
+ spin_unlock_irq(&bus->reg_lock);
+ unlock:
+ snd_hdac_dsp_unlock(azx_dev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
+
+/**
+ * snd_hdac_dsp_trigger - start / stop DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @start: trigger start or stop
+ */
+void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
+{
+ if (start)
+ snd_hdac_stream_start(azx_dev, true);
+ else
+ snd_hdac_stream_stop(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger);
+
+/**
+ * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @dmab: buffer used by DSP loading
+ */
+void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
+ struct snd_dma_buffer *dmab)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ if (!dmab->area || !azx_dev->locked)
+ return;
+
+ snd_hdac_dsp_lock(azx_dev);
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+ snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+
+ bus->io_ops->dma_free_pages(bus, dmab);
+ dmab->area = NULL;
+
+ spin_lock_irq(&bus->reg_lock);
+ azx_dev->locked = false;
+ spin_unlock_irq(&bus->reg_lock);
+ snd_hdac_dsp_unlock(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
diff --git a/sound/hda/trace.h b/sound/hda/trace.h
index 33a7eb5573d4..e27e2c0b7b17 100644
--- a/sound/hda/trace.h
+++ b/sound/hda/trace.h
@@ -50,6 +50,33 @@ TRACE_EVENT(hda_unsol_event,
),
TP_printk("%s", __get_str(msg))
);
+
+DECLARE_EVENT_CLASS(hdac_stream,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+
+ TP_ARGS(bus, azx_dev),
+
+ TP_STRUCT__entry(
+ __field(unsigned char, stream_tag)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_tag = (azx_dev)->stream_tag;
+ ),
+
+ TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_start,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+ TP_ARGS(bus, azx_dev)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_stop,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+ TP_ARGS(bus, azx_dev)
+);
+
#endif /* __HDAC_TRACE_H */
/* This part must be outside protection */
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index c65731088aa2..bf377dc192aa 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -859,7 +859,6 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0;
}
-#ifdef CONFIG_PROC_FS
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -884,9 +883,6 @@ static int proc_init(struct snd_akm4xxx *ak)
snd_info_set_text_ops(entry, ak, proc_regs_read);
return 0;
}
-#else /* !CONFIG_PROC_FS */
-static int proc_init(struct snd_akm4xxx *ak) { return 0; }
-#endif
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
index 0dd43414016e..3b5d9a7a63eb 100644
--- a/sound/isa/gus/gus_mixer.c
+++ b/sound/isa/gus/gus_mixer.c
@@ -109,7 +109,7 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
unsigned long flags;
int addr = kcontrol->private_value & 0xff;
int change;
- unsigned char val1, val2, oval1, oval2, tmp;
+ unsigned char val1, val2, oval1, oval2;
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
@@ -120,11 +120,8 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
gus->gf1.ics_regs[addr][0] = val1;
gus->gf1.ics_regs[addr][1] = val2;
if (gus->ics_flag && gus->ics_flipped &&
- (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) {
- tmp = val1;
- val1 = val2;
- val2 = tmp;
- }
+ (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV))
+ swap(val1, val2);
addr <<= 3;
outb(addr | 0, GUSP(gus, MIXCNTRLPORT));
outb(1, GUSP(gus, MIXDATAPORT));
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index ec1ee07df59d..10c8de1f8d29 100644
--- a/sound/oss/ad1848.c
+++ b/sound/oss/ad1848.c
@@ -2860,6 +2860,7 @@ static struct {
{NULL}
};
+#ifdef MODULE
static struct isapnp_device_id id_table[] = {
{ ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
@@ -2877,6 +2878,7 @@ static struct isapnp_device_id id_table[] = {
};
MODULE_DEVICE_TABLE(isapnp, id_table);
+#endif
static struct pnp_dev *activate_dev(char *devname, char *resname, struct pnp_dev *dev)
{
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index a8ceef8d1a8d..a8bb4a06ba6f 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -1288,8 +1288,7 @@ static int __init calibrate_adc(WORD srate)
& ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
- __set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ / 3);
+ schedule_timeout_interruptible(HZ / 3);
return 0;
}
printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c
index 048439a16000..dc91072f4d82 100644
--- a/sound/oss/sb_audio.c
+++ b/sound/oss/sb_audio.c
@@ -102,12 +102,8 @@ void sb_audio_close(int dev)
if(devc->duplex
&& !devc->fullduplex
&& (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE))
- {
- struct dma_buffparms *dmap_temp;
- dmap_temp = audio_devs[dev]->dmap_out;
- audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in;
- audio_devs[dev]->dmap_in = dmap_temp;
- }
+ swap(audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_in);
+
audio_devs[dev]->dmap_out->dma = devc->dma8;
audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ?
devc->dma16 : devc->dma8;
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 41fa322f0971..526175333710 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -4,7 +4,7 @@
#
snd-ac97-codec-y := ac97_codec.o ac97_pcm.o
-snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o
+snd-ac97-codec-$(CONFIG_SND_PROC_FS) += ac97_proc.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h
index c276a5e3f7ac..941a5062cc20 100644
--- a/sound/pci/ac97/ac97_local.h
+++ b/sound/pci/ac97/ac97_local.h
@@ -28,7 +28,7 @@ int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
unsigned short mask, unsigned short value);
/* ac97_proc.c */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void snd_ac97_bus_proc_init(struct snd_ac97_bus * ac97);
void snd_ac97_bus_proc_done(struct snd_ac97_bus * ac97);
void snd_ac97_proc_init(struct snd_ac97 * ac97);
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 66ddd981d1d5..1fc6d8bc09e5 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -898,8 +898,8 @@ snd_ad1889_create(struct snd_card *card,
return err;
/* check PCI availability (32bit DMA) */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
dev_err(card->dev, "error setting 32-bit DMA mask.\n");
pci_disable_device(pci);
return -ENXIO;
diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c
index 3bf0dc53360a..2fb1fbba3e5e 100644
--- a/sound/pci/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -35,11 +35,7 @@ MODULE_DESCRIPTION("Universal routines for AK4531 codec");
MODULE_LICENSE("GPL");
*/
-#ifdef CONFIG_PROC_FS
static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
-#else
-#define snd_ak4531_proc_init(card,ak)
-#endif
/*
*
@@ -466,7 +462,6 @@ void snd_ak4531_resume(struct snd_ak4531 *ak4531)
}
#endif
-#ifdef CONFIG_PROC_FS
/*
* /proc interface
*/
@@ -491,4 +486,3 @@ snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
if (! snd_card_proc_new(card, "ak4531", &entry))
snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
}
-#endif
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index c8d499575c01..36470af7eda7 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2105,8 +2105,8 @@ static int snd_ali_create(struct snd_card *card,
if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 31 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(31)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(31)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(31)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(31)) < 0) {
dev_err(card->dev,
"architecture does not support 31bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 57e034f208dc..add3176398d3 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -658,8 +658,8 @@ static int snd_als300_create(struct snd_card *card,
if ((err = pci_enable_device(pci)) < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
dev_err(card->dev, "error setting 28bit DMA mask\n");
pci_disable_device(pci);
return -ENXIO;
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index a3dea464134d..ff39a0c7277b 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -871,8 +871,8 @@ static int snd_card_als4000_probe(struct pci_dev *pci,
return err;
}
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
dev_err(&pci->dev, "architecture does not support 24bit PCI busmaster DMA\n");
pci_disable_device(pci);
return -ENXIO;
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 42a20c806b39..1028fc8bdff5 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1530,7 +1530,6 @@ static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PROC_FS
/*
* proc interface for register dump
*/
@@ -1552,9 +1551,6 @@ static void snd_atiixp_proc_init(struct atiixp *chip)
if (! snd_card_proc_new(chip->card, "atiixp", &entry))
snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-#define snd_atiixp_proc_init(chip)
-#endif
/*
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 0a38e08164ab..27ed678a46df 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1156,7 +1156,6 @@ static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
#define SND_ATIIXP_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PROC_FS
/*
* proc interface for register dump
*/
@@ -1178,9 +1177,6 @@ static void snd_atiixp_proc_init(struct atiixp_modem *chip)
if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry))
snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
}
-#else
-#define snd_atiixp_proc_init(chip)
-#endif
/*
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 996369134ea8..32092184bbf2 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -150,8 +150,8 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
// check PCI availability (DMA).
if ((err = pci_enable_device(pci)) < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
dev_err(card->dev, "error to set DMA mask\n");
pci_disable_device(pci);
return -ENXIO;
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 8d2fee7b33bd..167714303070 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -258,8 +258,8 @@ static int snd_aw2_create(struct snd_card *card,
pci_set_master(pci);
/* check PCI availability (32bit DMA) */
- if ((pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) ||
- (pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0)) {
+ if ((dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) ||
+ (dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0)) {
dev_err(card->dev, "Impossible to set 32bit mask DMA\n");
pci_disable_device(pci);
return -ENXIO;
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 33b2a0af1b59..07a4acc99541 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2420,8 +2420,8 @@ snd_azf3328_create(struct snd_card *card,
chip->irq = -1;
/* check if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
dev_err(card->dev,
"architecture does not support 24bit PCI busmaster DMA\n"
);
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
index dcbae7b31546..c1455fc5588c 100644
--- a/sound/pci/ca0106/Makefile
+++ b/sound/pci/ca0106/Makefile
@@ -1,3 +1,4 @@
-snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o ca_midi.o
+snd-ca0106-objs := ca0106_main.o ca0106_mixer.o ca_midi.o
+snd-ca0106-$(CONFIG_SND_PROC_FS) += ca0106_proc.o
obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index dd75b7536fa2..d3cd95633ee2 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1676,8 +1676,8 @@ static int snd_ca0106_create(int dev, struct snd_card *card,
err = pci_enable_device(pci);
if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
dev_err(card->dev, "error to set 32bit mask DMA\n");
pci_disable_device(pci);
return -ENXIO;
@@ -1885,7 +1885,7 @@ static int snd_ca0106_probe(struct pci_dev *pci,
goto error;
dev_dbg(card->dev, " done.\n");
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_ca0106_proc_init(chip);
#endif
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index 2c5c28adbefd..9b2b8b38122f 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -75,8 +75,6 @@
#include "ca0106.h"
-#ifdef CONFIG_PROC_FS
-
struct snd_ca0106_category_str {
int val;
const char *name;
@@ -453,5 +451,3 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu)
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
return 0;
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 6cf464d9043d..24cdcba06d27 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2772,7 +2772,6 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
* proc interface
*/
-#ifdef CONFIG_PROC_FS
static void snd_cmipci_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -2798,10 +2797,6 @@ static void snd_cmipci_proc_init(struct cmipci *cm)
if (! snd_card_proc_new(cm->card, "cmipci", &entry))
snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
-#endif
-
static const struct pci_device_id snd_cmipci_ids[] = {
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0},
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 8d74004b1ed2..2a9f4a345171 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2816,7 +2816,7 @@ int snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
#endif /* CONFIG_GAMEPORT */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -2865,7 +2865,7 @@ static int snd_cs46xx_proc_done(struct snd_cs46xx *chip)
#endif
return 0;
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_cs46xx_proc_init(card, chip)
#define snd_cs46xx_proc_done(chip)
#endif
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
index 86f14620f817..bdf4114167ea 100644
--- a/sound/pci/cs46xx/cs46xx_lib.h
+++ b/sound/pci/cs46xx/cs46xx_lib.h
@@ -95,7 +95,7 @@ int cs46xx_dsp_resume(struct snd_cs46xx * chip);
#endif
struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
int symbol_type);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip);
int cs46xx_dsp_proc_done (struct snd_cs46xx *chip);
#else
@@ -118,7 +118,7 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip);
int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data);
struct dsp_scb_descriptor * cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name,
u32 * scb_data, u32 dest);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb);
void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
struct dsp_scb_descriptor * scb);
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index 5c99efb004c0..d2951ed4bf71 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -476,7 +476,7 @@ cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symb
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct dsp_symbol_entry *
cs46xx_dsp_lookup_symbol_addr (struct snd_cs46xx * chip, u32 address, int symbol_type)
{
@@ -929,7 +929,7 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
return 0;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data,
u32 dest, int size)
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 2c90c0bded69..7488e1b7a770 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -67,7 +67,7 @@ static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * s
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -228,7 +228,7 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
{
if (scb->proc_info) {
@@ -285,7 +285,7 @@ out:
scb->proc_info = entry;
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static struct dsp_scb_descriptor *
_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest,
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 963b912550d4..de409cda50aa 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -289,8 +289,8 @@ static int snd_cs5535audio_create(struct snd_card *card,
if ((err = pci_enable_device(pci)) < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
dev_warn(card->dev, "unable to get 32bit dma\n");
err = -ENXIO;
goto pcifail;
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 1cac55fd1139..9667cbfb0ca2 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1910,8 +1910,8 @@ static int hw_card_start(struct hw *hw)
return err;
/* Set DMA transfer mask */
- if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
+ if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 ||
+ dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) {
dev_err(hw->card->dev,
"architecture does not support PCI busmaster DMA with mask 0x%llx\n",
CT_XFI_DMA_MASK);
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 955ad871e9a8..9dc2950e1ab7 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -2035,8 +2035,8 @@ static int hw_card_start(struct hw *hw)
return err;
/* Set DMA transfer mask */
- if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
+ if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 ||
+ dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) {
dev_err(hw->card->dev,
"architecture does not support PCI busmaster DMA with mask 0x%llx\n",
CT_XFI_DMA_MASK);
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
index fc5591e7777e..29b44ca27010 100644
--- a/sound/pci/emu10k1/Makefile
+++ b/sound/pci/emu10k1/Makefile
@@ -5,7 +5,8 @@
snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
- emuproc.o emumixer.o emufx.o timer.o p16v.o
+ emumixer.o emufx.o timer.o p16v.o
+snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o
snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
snd-emu10k1x-objs := emu10k1x.o
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index a4548147c621..28e2f8b42f5e 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1911,8 +1911,8 @@ int snd_emu10k1_create(struct snd_card *card,
emu->address_mode = is_audigy ? 0 : 1;
/* set the DMA transfer mask */
emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
- if (pci_set_dma_mask(pci, emu->dma_mask) < 0 ||
- pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) {
+ if (dma_set_mask(&pci->dev, emu->dma_mask) < 0 ||
+ dma_set_coherent_mask(&pci->dev, emu->dma_mask) < 0) {
dev_err(card->dev,
"architecture does not support PCI busmaster DMA with mask 0x%lx\n",
emu->dma_mask);
@@ -2063,7 +2063,7 @@ int snd_emu10k1_create(struct snd_card *card,
if (err < 0)
goto error;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_emu10k1_proc_init(emu);
#endif
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index 53745f4c2bf5..cf05229b569b 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -34,7 +34,6 @@
#include <sound/emu10k1.h>
#include "p16v.h"
-#ifdef CONFIG_PROC_FS
static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
struct snd_info_buffer *buffer,
char *title,
@@ -656,4 +655,3 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
}
return 0;
}
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index e1858d9d23d8..8963d7688fb0 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1580,8 +1580,8 @@ static int snd_es1938_create(struct snd_card *card,
if ((err = pci_enable_device(pci)) < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
dev_err(card->dev,
"architecture does not support 24bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 059f3846d7b8..e0d9363dc7fd 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2689,8 +2689,8 @@ static int snd_es1968_create(struct snd_card *card,
if ((err = pci_enable_device(pci)) < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 28 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
dev_err(card->dev,
"architecture does not support 28bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index a5ed1c181784..e94cfd5c69f7 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -4,7 +4,7 @@ config SND_HDA
tristate
select SND_PCM
select SND_VMASTER
- select SND_KCTL_JACK
+ select SND_JACK if INPUT=y || INPUT=SND
select SND_HDA_CORE
config SND_HDA_INTEL
@@ -38,22 +38,6 @@ config SND_HDA_TEGRA
if SND_HDA
-config SND_HDA_DSP_LOADER
- bool
-
-config SND_HDA_PREALLOC_SIZE
- int "Pre-allocated buffer size for HD-audio driver"
- range 0 32768
- default 64
- help
- Specifies the default pre-allocated buffer-size in kB for the
- HD-audio driver. A larger buffer (e.g. 2048) is preferred
- for systems using PulseAudio. The default 64 is chosen just
- for compatibility reasons.
-
- Note that the pre-allocation size can be changed dynamically
- via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
-
config SND_HDA_HWDEP
bool "Build hwdep interface for HD-audio driver"
select SND_HWDEP
@@ -87,14 +71,6 @@ config SND_HDA_INPUT_BEEP_MODE
Set 1 to always enable the digital beep interface for HD-audio by
default.
-config SND_HDA_INPUT_JACK
- bool "Support jack plugging notification via input layer"
- depends on INPUT=y || INPUT=SND
- select SND_JACK
- help
- Say Y here to enable the jack plugging notification via
- input layer.
-
config SND_HDA_PATCH_LOADER
bool "Support initialization patch loading for HD-audio"
select FW_LOADER
@@ -156,11 +132,6 @@ config SND_HDA_CODEC_HDMI
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
-config SND_HDA_I915
- bool
- default y
- depends on DRM_I915
-
config SND_HDA_CODEC_CIRRUS
tristate "Build Cirrus Logic codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index af78fb33a4fd..6d83c6e0396a 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,16 +1,16 @@
snd-hda-intel-objs := hda_intel.o
-snd-hda-controller-objs := hda_controller.o
snd-hda-tegra-objs := hda_tegra.o
-# for haswell power well
-snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
-snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
+snd-hda-codec-y += hda_controller.o
+snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o
+
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
# for trace-points
CFLAGS_hda_controller.o := -I$(src)
+CFLAGS_hda_intel.o := -I$(src)
snd-hda-codec-generic-objs := hda_generic.o
snd-hda-codec-realtek-objs := patch_realtek.o
@@ -27,7 +27,6 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
-obj-$(CONFIG_SND_HDA) += snd-hda-controller.o
# codec drivers
obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 3364dc0fdeab..c397e7da0eac 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -1,7 +1,7 @@
/*
* Digital Beep Input Interface for HD-audio codec
*
- * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Author: Matt Ranostay <mranostay@gmail.com>
* Copyright (c) 2008 Embedded Alley Solutions Inc
*
* This driver is free software; you can redistribute it and/or modify
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index 46524ff7e79e..1052ad380e97 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -1,7 +1,7 @@
/*
* Digital Beep Input Interface for HD-audio codec
*
- * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Author: Matt Ranostay <mranostay@gmail.com>
* Copyright (c) 2008 Embedded Alley Solutions Inc
*
* This driver is free software; you can redistribute it and/or modify
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 00aa31c5f08e..d5ac25cc7fee 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -27,15 +27,7 @@ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
for (preset = driver->preset; preset->id; preset++) {
- u32 mask = preset->mask;
-
- if (preset->afg && preset->afg != codec->core.afg)
- continue;
- if (preset->mfg && preset->mfg != codec->core.mfg)
- continue;
- if (!mask)
- mask = ~0;
- if (preset->id == (id & mask) &&
+ if (preset->id == id &&
(!preset->rev || preset->rev == codec->core.revision_id)) {
codec->preset = preset;
return 1;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 5645481af3d9..5de3c5d8c2c0 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -146,11 +146,11 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
bus->no_response_fallback = 0;
mutex_unlock(&bus->core.cmd_mutex);
snd_hda_power_down_pm(codec);
- if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) {
+ if (!codec_in_pm(codec) && res && err == -EAGAIN) {
if (bus->response_reset) {
codec_dbg(codec,
"resetting BUS due to fatal communication error\n");
- bus->ops.bus_reset(bus);
+ snd_hda_bus_reset(bus);
}
goto again;
}
@@ -437,7 +437,7 @@ static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
return 0;
parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN);
- if (parm == -1 && codec->bus->rirb_error)
+ if (parm == -1)
parm = 0;
return parm & AC_DEV_LIST_LEN_MASK;
}
@@ -467,10 +467,9 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
devices = 0;
while (devices < dev_len) {
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DEVICE_LIST, devices);
- if (parm == -1 && codec->bus->rirb_error)
- break;
+ if (snd_hdac_read(&codec->core, nid,
+ AC_VERB_GET_DEVICE_LIST, devices, &parm))
+ break; /* error */
for (i = 0; i < 8; i++) {
dev_list[devices] = (u8)parm;
@@ -484,96 +483,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
}
/*
- * destructor
- */
-static void snd_hda_bus_free(struct hda_bus *bus)
-{
- if (!bus)
- return;
- if (bus->ops.private_free)
- bus->ops.private_free(bus);
- snd_hdac_bus_exit(&bus->core);
- kfree(bus);
-}
-
-static int snd_hda_bus_dev_free(struct snd_device *device)
-{
- snd_hda_bus_free(device->device_data);
- return 0;
-}
-
-static int snd_hda_bus_dev_disconnect(struct snd_device *device)
-{
- struct hda_bus *bus = device->device_data;
- bus->shutdown = 1;
- return 0;
-}
-
-/* hdac_bus_ops translations */
-static int _hda_bus_command(struct hdac_bus *_bus, unsigned int cmd)
-{
- struct hda_bus *bus = container_of(_bus, struct hda_bus, core);
- return bus->ops.command(bus, cmd);
-}
-
-static int _hda_bus_get_response(struct hdac_bus *_bus, unsigned int addr,
- unsigned int *res)
-{
- struct hda_bus *bus = container_of(_bus, struct hda_bus, core);
- *res = bus->ops.get_response(bus, addr);
- return bus->rirb_error ? -EIO : 0;
-}
-
-static const struct hdac_bus_ops bus_ops = {
- .command = _hda_bus_command,
- .get_response = _hda_bus_get_response,
-};
-
-/**
- * snd_hda_bus_new - create a HDA bus
- * @card: the card entry
- * @busp: the pointer to store the created bus instance
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_bus_new(struct snd_card *card,
- struct hda_bus **busp)
-{
- struct hda_bus *bus;
- int err;
- static struct snd_device_ops dev_ops = {
- .dev_disconnect = snd_hda_bus_dev_disconnect,
- .dev_free = snd_hda_bus_dev_free,
- };
-
- if (busp)
- *busp = NULL;
-
- bus = kzalloc(sizeof(*bus), GFP_KERNEL);
- if (!bus)
- return -ENOMEM;
-
- err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops);
- if (err < 0) {
- kfree(bus);
- return err;
- }
-
- bus->card = card;
- mutex_init(&bus->prepare_mutex);
-
- err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
- if (err < 0) {
- snd_hda_bus_free(bus);
- return err;
- }
- if (busp)
- *busp = bus;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_bus_new);
-
-/*
* read widget caps for each widget and store in cache
*/
static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
@@ -950,6 +859,7 @@ void snd_hda_codec_register(struct hda_codec *codec)
return;
if (device_is_registered(hda_codec_dev(codec))) {
snd_hda_register_beep_device(codec);
+ snd_hdac_link_power(&codec->core, true);
pm_runtime_enable(hda_codec_dev(codec));
/* it was powered up in snd_hda_codec_new(), now all done */
snd_hda_power_down(codec);
@@ -977,6 +887,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
codec->in_freeing = 1;
snd_hdac_device_unregister(&codec->core);
+ snd_hdac_link_power(&codec->core, false);
put_device(hda_codec_dev(codec));
return 0;
}
@@ -3223,6 +3134,7 @@ static int hda_codec_runtime_suspend(struct device *dev)
if (codec_has_clkstop(codec) && codec_has_epss(codec) &&
(state & AC_PWRST_CLK_STOP_OK))
snd_hdac_codec_link_down(&codec->core);
+ snd_hdac_link_power(&codec->core, false);
return 0;
}
@@ -3230,6 +3142,7 @@ static int hda_codec_runtime_resume(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
+ snd_hdac_link_power(&codec->core, true);
snd_hdac_codec_link_up(&codec->core);
hda_call_codec_resume(codec);
pm_runtime_mark_last_busy(dev);
@@ -3313,311 +3226,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
}
/*
- * stream formats
- */
-struct hda_rate_tbl {
- unsigned int hz;
- unsigned int alsa_bits;
- unsigned int hda_fmt;
-};
-
-/* rate = base * mult / div */
-#define HDA_RATE(base, mult, div) \
- (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
- (((div) - 1) << AC_FMT_DIV_SHIFT))
-
-static struct hda_rate_tbl rate_bits[] = {
- /* rate in Hz, ALSA rate bitmask, HDA format value */
-
- /* autodetected value used in snd_hda_query_supported_pcm */
- { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
- { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
- { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
- { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
- { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
- { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
- { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
- { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
- { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
- { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
- { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
-#define AC_PAR_PCM_RATE_BITS 11
- /* up to bits 10, 384kHZ isn't supported properly */
-
- /* not autodetected value */
- { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
-
- { 0 } /* terminator */
-};
-
-/**
- * snd_hda_calc_stream_format - calculate format bitset
- * @codec: HD-audio codec
- * @rate: the sample rate
- * @channels: the number of channels
- * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
- * @maxbps: the max. bps
- * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
- *
- * Calculate the format bitset from the given rate, channels and th PCM format.
- *
- * Return zero if invalid.
- */
-unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
- unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls)
-{
- int i;
- unsigned int val = 0;
-
- for (i = 0; rate_bits[i].hz; i++)
- if (rate_bits[i].hz == rate) {
- val = rate_bits[i].hda_fmt;
- break;
- }
- if (!rate_bits[i].hz) {
- codec_dbg(codec, "invalid rate %d\n", rate);
- return 0;
- }
-
- if (channels == 0 || channels > 8) {
- codec_dbg(codec, "invalid channels %d\n", channels);
- return 0;
- }
- val |= channels - 1;
-
- switch (snd_pcm_format_width(format)) {
- case 8:
- val |= AC_FMT_BITS_8;
- break;
- case 16:
- val |= AC_FMT_BITS_16;
- break;
- case 20:
- case 24:
- case 32:
- if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
- val |= AC_FMT_BITS_32;
- else if (maxbps >= 24)
- val |= AC_FMT_BITS_24;
- else
- val |= AC_FMT_BITS_20;
- break;
- default:
- codec_dbg(codec, "invalid format width %d\n",
- snd_pcm_format_width(format));
- return 0;
- }
-
- if (spdif_ctls & AC_DIG1_NONAUDIO)
- val |= AC_FMT_TYPE_NON_PCM;
-
- return val;
-}
-EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format);
-
-static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int val = 0;
- if (nid != codec->core.afg &&
- (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
- val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- if (!val || val == -1)
- val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM);
- if (!val || val == -1)
- return 0;
- return val;
-}
-
-static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
- if (!streams || streams == -1)
- streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM);
- if (!streams || streams == -1)
- return 0;
- return streams;
-}
-
-/**
- * snd_hda_query_supported_pcm - query the supported PCM rates and formats
- * @codec: the HDA codec
- * @nid: NID to query
- * @ratesp: the pointer to store the detected rate bitflags
- * @formatsp: the pointer to store the detected formats
- * @bpsp: the pointer to store the detected format widths
- *
- * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
- * or @bsps argument is ignored.
- *
- * Returns 0 if successful, otherwise a negative error code.
- */
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
-{
- unsigned int i, val, wcaps;
-
- wcaps = get_wcaps(codec, nid);
- val = query_pcm_param(codec, nid);
-
- if (ratesp) {
- u32 rates = 0;
- for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
- if (val & (1 << i))
- rates |= rate_bits[i].alsa_bits;
- }
- if (rates == 0) {
- codec_err(codec,
- "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
- return -EIO;
- }
- *ratesp = rates;
- }
-
- if (formatsp || bpsp) {
- u64 formats = 0;
- unsigned int streams, bps;
-
- streams = query_stream_param(codec, nid);
- if (!streams)
- return -EIO;
-
- bps = 0;
- if (streams & AC_SUPFMT_PCM) {
- if (val & AC_SUPPCM_BITS_8) {
- formats |= SNDRV_PCM_FMTBIT_U8;
- bps = 8;
- }
- if (val & AC_SUPPCM_BITS_16) {
- formats |= SNDRV_PCM_FMTBIT_S16_LE;
- bps = 16;
- }
- if (wcaps & AC_WCAP_DIGITAL) {
- if (val & AC_SUPPCM_BITS_32)
- formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
- if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
- AC_SUPPCM_BITS_32)) {
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_32)
- bps = 32;
- else if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- }
- }
-#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
- if (streams & AC_SUPFMT_FLOAT32) {
- formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- if (!bps)
- bps = 32;
- }
-#endif
- if (streams == AC_SUPFMT_AC3) {
- /* should be exclusive */
- /* temporary hack: we have still no proper support
- * for the direct AC3 stream...
- */
- formats |= SNDRV_PCM_FMTBIT_U8;
- bps = 8;
- }
- if (formats == 0) {
- codec_err(codec,
- "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
- streams);
- return -EIO;
- }
- if (formatsp)
- *formatsp = formats;
- if (bpsp)
- *bpsp = bps;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm);
-
-/**
- * snd_hda_is_supported_format - Check the validity of the format
- * @codec: HD-audio codec
- * @nid: NID to check
- * @format: the HD-audio format value to check
- *
- * Check whether the given node supports the format value.
- *
- * Returns 1 if supported, 0 if not.
- */
-int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned int format)
-{
- int i;
- unsigned int val = 0, rate, stream;
-
- val = query_pcm_param(codec, nid);
- if (!val)
- return 0;
-
- rate = format & 0xff00;
- for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
- if (rate_bits[i].hda_fmt == rate) {
- if (val & (1 << i))
- break;
- return 0;
- }
- if (i >= AC_PAR_PCM_RATE_BITS)
- return 0;
-
- stream = query_stream_param(codec, nid);
- if (!stream)
- return 0;
-
- if (stream & AC_SUPFMT_PCM) {
- switch (format & 0xf0) {
- case 0x00:
- if (!(val & AC_SUPPCM_BITS_8))
- return 0;
- break;
- case 0x10:
- if (!(val & AC_SUPPCM_BITS_16))
- return 0;
- break;
- case 0x20:
- if (!(val & AC_SUPPCM_BITS_20))
- return 0;
- break;
- case 0x30:
- if (!(val & AC_SUPPCM_BITS_24))
- return 0;
- break;
- case 0x40:
- if (!(val & AC_SUPPCM_BITS_32))
- return 0;
- break;
- default:
- return 0;
- }
- } else {
- /* FIXME: check for float32 and AC3? */
- }
-
- return 1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_is_supported_format);
-
-/*
* PCM stuff
*/
static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
@@ -3829,9 +3437,6 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
struct hda_pcm *cpcm;
int dev, err;
- if (snd_BUG_ON(!bus->ops.attach_pcm))
- return -EINVAL;
-
err = snd_hda_codec_parse_pcms(codec);
if (err < 0) {
snd_hda_codec_reset(codec);
@@ -3849,7 +3454,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
if (dev < 0)
continue; /* no fatal error */
cpcm->device = dev;
- err = bus->ops.attach_pcm(bus, codec, cpcm);
+ err = snd_hda_attach_pcm_stream(bus, codec, cpcm);
if (err < 0) {
codec_err(codec,
"cannot attach PCM stream %d for codec #%d\n",
@@ -3916,6 +3521,9 @@ static void codec_set_power_save(struct hda_codec *codec, int delay)
{
struct device *dev = hda_codec_dev(codec);
+ if (delay == 0 && codec->auto_runtime_pm)
+ delay = 3000;
+
if (delay > 0) {
pm_runtime_set_autosuspend_delay(dev, delay);
pm_runtime_use_autosuspend(dev);
@@ -4519,10 +4127,10 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
/**
- * snd_hda_bus_reset - Reset the bus
+ * snd_hda_bus_reset_codecs - Reset the bus
* @bus: HD-audio bus
*/
-void snd_hda_bus_reset(struct hda_bus *bus)
+void snd_hda_bus_reset_codecs(struct hda_bus *bus)
{
struct hda_codec *codec;
@@ -4537,7 +4145,6 @@ void snd_hda_bus_reset(struct hda_bus *bus)
#endif
}
}
-EXPORT_SYMBOL_GPL(snd_hda_bus_reset);
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 9075ac28dc4b..12837abbbbe5 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -40,32 +40,6 @@ struct hda_codec;
struct hda_pcm;
struct hda_pcm_stream;
-/* bus operators */
-struct hda_bus_ops {
- /* send a single command */
- int (*command)(struct hda_bus *bus, unsigned int cmd);
- /* get a response from the last command */
- unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
- /* free the private data */
- void (*private_free)(struct hda_bus *);
- /* attach a PCM stream */
- int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *pcm);
- /* reset bus for retry verb */
- void (*bus_reset)(struct hda_bus *bus);
-#ifdef CONFIG_SND_HDA_DSP_LOADER
- /* prepare DSP transfer */
- int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
- unsigned int byte_size,
- struct snd_dma_buffer *bufp);
- /* start/stop DSP transfer */
- void (*load_dsp_trigger)(struct hda_bus *bus, bool start);
- /* clean up DSP transfer */
- void (*load_dsp_cleanup)(struct hda_bus *bus,
- struct snd_dma_buffer *dmab);
-#endif
-};
-
/*
* codec bus
*
@@ -77,10 +51,8 @@ struct hda_bus {
struct snd_card *card;
- void *private_data;
struct pci_dev *pci;
const char *modelname;
- struct hda_bus_ops ops;
struct mutex prepare_mutex;
@@ -92,7 +64,6 @@ struct hda_bus {
unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
/* status for codec/controller */
unsigned int shutdown :1; /* being unloaded */
- unsigned int rirb_error:1; /* error in codec communication */
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
@@ -100,6 +71,9 @@ struct hda_bus {
int primary_dig_out_type; /* primary digital out PCM type */
};
+/* from hdac_bus to hda_bus */
+#define to_hda_bus(bus) container_of(bus, struct hda_bus, core)
+
/*
* codec preset
*
@@ -108,11 +82,7 @@ struct hda_bus {
*/
struct hda_codec_preset {
unsigned int id;
- unsigned int mask;
- unsigned int subs;
- unsigned int subs_mask;
unsigned int rev;
- hda_nid_t afg, mfg;
const char *name;
int (*patch)(struct hda_codec *codec);
};
@@ -281,6 +251,7 @@ struct hda_codec {
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
unsigned int power_save_node:1; /* advanced PM for each widget */
+ unsigned int auto_runtime_pm:1; /* enable automatic codec runtime pm */
#ifdef CONFIG_PM
unsigned long power_on_acct;
unsigned long power_off_acct;
@@ -300,10 +271,8 @@ struct hda_codec {
unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
struct delayed_work jackpoll_work;
-#ifdef CONFIG_SND_HDA_INPUT_JACK
/* jack detection */
struct snd_array jacks;
-#endif
int depop_delay; /* depop delay in ms, -1 for default delay time */
@@ -328,7 +297,10 @@ struct hda_codec {
/*
* constructors
*/
-int snd_hda_bus_new(struct snd_card *card, struct hda_bus **busp);
+int snd_hda_bus_new(struct snd_card *card,
+ const struct hdac_bus_ops *ops,
+ const struct hdac_io_ops *io_ops,
+ struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
unsigned int codec_addr, struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
@@ -367,8 +339,6 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive);
int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
u8 *dev_list, int max_devices);
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
struct hda_verb {
hda_nid_t nid;
@@ -460,17 +430,17 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
int do_now);
#define snd_hda_codec_cleanup_stream(codec, nid) \
__snd_hda_codec_cleanup_stream(codec, nid, 0)
-unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
- unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls);
-int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned int format);
+
+#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \
+ snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp)
+#define snd_hda_is_supported_format(codec, nid, fmt) \
+ snd_hdac_is_supported_format(&(codec)->core, nid, fmt)
extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
+int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm);
+
/*
* Misc
*/
@@ -481,6 +451,7 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus);
void snd_hda_bus_reset(struct hda_bus *bus);
+void snd_hda_bus_reset_codecs(struct hda_bus *bus);
/*
* power management
@@ -526,24 +497,12 @@ int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
#ifdef CONFIG_SND_HDA_DSP_LOADER
-static inline int
-snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
- unsigned int size,
- struct snd_dma_buffer *bufp)
-{
- return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp);
-}
-static inline void
-snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
-{
- return codec->bus->ops.load_dsp_trigger(codec->bus, start);
-}
-static inline void
-snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
- struct snd_dma_buffer *dmab)
-{
- return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab);
-}
+int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+ unsigned int size,
+ struct snd_dma_buffer *bufp);
+void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start);
+void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+ struct snd_dma_buffer *dmab);
#else
static inline int
snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 26ce990592a0..944455997fdc 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -32,229 +32,29 @@
#include "hda_controller.h"
#define CREATE_TRACE_POINTS
-#include "hda_intel_trace.h"
+#include "hda_controller_trace.h"
/* DSP lock helpers */
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-#define dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex)
-#define dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex)
-#define dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex)
-#define dsp_is_locked(dev) ((dev)->locked)
-#else
-#define dsp_lock_init(dev) do {} while (0)
-#define dsp_lock(dev) do {} while (0)
-#define dsp_unlock(dev) do {} while (0)
-#define dsp_is_locked(dev) 0
-#endif
-
-/*
- * AZX stream operations.
- */
-
-/* start a stream */
-static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
-{
- /*
- * Before stream start, initialize parameter
- */
- azx_dev->insufficient = 1;
-
- /* enable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) | (1 << azx_dev->index));
- /* set DMA start and interrupt mask */
- azx_sd_writeb(chip, azx_dev, SD_CTL,
- azx_sd_readb(chip, azx_dev, SD_CTL) |
- SD_CTL_DMA_START | SD_INT_MASK);
-}
-
-/* stop DMA */
-static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
-{
- azx_sd_writeb(chip, azx_dev, SD_CTL,
- azx_sd_readb(chip, azx_dev, SD_CTL) &
- ~(SD_CTL_DMA_START | SD_INT_MASK));
- azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
-}
-
-/* stop a stream */
-void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
-{
- azx_stream_clear(chip, azx_dev);
- /* disable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
-}
-EXPORT_SYMBOL_GPL(azx_stream_stop);
-
-/* reset stream */
-static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
-{
- unsigned char val;
- int timeout;
-
- azx_stream_clear(chip, azx_dev);
-
- azx_sd_writeb(chip, azx_dev, SD_CTL,
- azx_sd_readb(chip, azx_dev, SD_CTL) |
- SD_CTL_STREAM_RESET);
- udelay(3);
- timeout = 300;
- while (!((val = azx_sd_readb(chip, azx_dev, SD_CTL)) &
- SD_CTL_STREAM_RESET) && --timeout)
- ;
- val &= ~SD_CTL_STREAM_RESET;
- azx_sd_writeb(chip, azx_dev, SD_CTL, val);
- udelay(3);
-
- timeout = 300;
- /* waiting for hardware to report that the stream is out of reset */
- while (((val = azx_sd_readb(chip, azx_dev, SD_CTL)) &
- SD_CTL_STREAM_RESET) && --timeout)
- ;
-
- /* reset first position - may not be synced with hw at this time */
- *azx_dev->posbuf = 0;
-}
-
-/*
- * set up the SD for streaming
- */
-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
-{
- unsigned int val;
- /* make sure the run bit is zero for SD */
- azx_stream_clear(chip, azx_dev);
- /* program the stream_tag */
- val = azx_sd_readl(chip, azx_dev, SD_CTL);
- val = (val & ~SD_CTL_STREAM_TAG_MASK) |
- (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
- if (!azx_snoop(chip))
- val |= SD_CTL_TRAFFIC_PRIO;
- azx_sd_writel(chip, azx_dev, SD_CTL, val);
-
- /* program the length of samples in cyclic buffer */
- azx_sd_writel(chip, azx_dev, SD_CBL, azx_dev->bufsize);
-
- /* program the stream format */
- /* this value needs to be the same as the one programmed */
- azx_sd_writew(chip, azx_dev, SD_FORMAT, azx_dev->format_val);
-
- /* program the stream LVI (last valid index) of the BDL */
- azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->frags - 1);
-
- /* program the BDL address */
- /* lower BDL address */
- azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
- /* upper BDL address */
- azx_sd_writel(chip, azx_dev, SD_BDLPU,
- upper_32_bits(azx_dev->bdl.addr));
-
- /* enable the position buffer */
- if (chip->get_position[0] != azx_get_pos_lpib ||
- chip->get_position[1] != azx_get_pos_lpib) {
- if (!(azx_readl(chip, DPLBASE) & AZX_DPLBASE_ENABLE))
- azx_writel(chip, DPLBASE,
- (u32)chip->posbuf.addr | AZX_DPLBASE_ENABLE);
- }
-
- /* set the interrupt enable bits in the descriptor control register */
- azx_sd_writel(chip, azx_dev, SD_CTL,
- azx_sd_readl(chip, azx_dev, SD_CTL) | SD_INT_MASK);
-
- return 0;
-}
+#define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev))
+#define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev))
+#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev))
/* assign a stream for the PCM */
static inline struct azx_dev *
azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
{
- int dev, i, nums;
- struct azx_dev *res = NULL;
- /* make a non-zero unique key for the substream */
- int key = (substream->pcm->device << 16) | (substream->number << 2) |
- (substream->stream + 1);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev = chip->playback_index_offset;
- nums = chip->playback_streams;
- } else {
- dev = chip->capture_index_offset;
- nums = chip->capture_streams;
- }
- for (i = 0; i < nums; i++, dev++) {
- struct azx_dev *azx_dev = &chip->azx_dev[dev];
- dsp_lock(azx_dev);
- if (!azx_dev->opened && !dsp_is_locked(azx_dev)) {
- if (azx_dev->assigned_key == key) {
- azx_dev->opened = 1;
- azx_dev->assigned_key = key;
- dsp_unlock(azx_dev);
- return azx_dev;
- }
- if (!res ||
- (chip->driver_caps & AZX_DCAPS_REVERSE_ASSIGN))
- res = azx_dev;
- }
- dsp_unlock(azx_dev);
- }
- if (res) {
- dsp_lock(res);
- res->opened = 1;
- res->assigned_key = key;
- dsp_unlock(res);
- }
- return res;
+ struct hdac_stream *s;
+
+ s = snd_hdac_stream_assign(azx_bus(chip), substream);
+ if (!s)
+ return NULL;
+ return stream_to_azx_dev(s);
}
/* release the assigned stream */
static inline void azx_release_device(struct azx_dev *azx_dev)
{
- azx_dev->opened = 0;
-}
-
-static cycle_t azx_cc_read(const struct cyclecounter *cc)
-{
- struct azx_dev *azx_dev = container_of(cc, struct azx_dev, azx_cc);
- struct snd_pcm_substream *substream = azx_dev->substream;
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
-
- return azx_readl(chip, WALLCLK);
-}
-
-static void azx_timecounter_init(struct snd_pcm_substream *substream,
- bool force, cycle_t last)
-{
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct timecounter *tc = &azx_dev->azx_tc;
- struct cyclecounter *cc = &azx_dev->azx_cc;
- u64 nsec;
-
- cc->read = azx_cc_read;
- cc->mask = CLOCKSOURCE_MASK(32);
-
- /*
- * Converting from 24 MHz to ns means applying a 125/3 factor.
- * To avoid any saturation issues in intermediate operations,
- * the 125 factor is applied first. The division is applied
- * last after reading the timecounter value.
- * Applying the 1/3 factor as part of the multiplication
- * requires at least 20 bits for a decent precision, however
- * overflows occur after about 4 hours or less, not a option.
- */
-
- cc->mult = 125; /* saturation after 195 years */
- cc->shift = 0;
-
- nsec = 0; /* audio time is elapsed time since trigger */
- timecounter_init(tc, cc, nsec);
- if (force)
- /*
- * force timecounter to use predefined value,
- * used for synchronized starts
- */
- tc->cycle_last = last;
+ snd_hdac_stream_release(azx_stream(azx_dev));
}
static inline struct hda_pcm_stream *
@@ -285,119 +85,6 @@ static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
}
/*
- * set up a BDL entry
- */
-static int setup_bdle(struct azx *chip,
- struct snd_dma_buffer *dmab,
- struct azx_dev *azx_dev, u32 **bdlp,
- int ofs, int size, int with_ioc)
-{
- u32 *bdl = *bdlp;
-
- while (size > 0) {
- dma_addr_t addr;
- int chunk;
-
- if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
- return -EINVAL;
-
- addr = snd_sgbuf_get_addr(dmab, ofs);
- /* program the address field of the BDL entry */
- bdl[0] = cpu_to_le32((u32)addr);
- bdl[1] = cpu_to_le32(upper_32_bits(addr));
- /* program the size field of the BDL entry */
- chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
- /* one BDLE cannot cross 4K boundary on CTHDA chips */
- if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
- u32 remain = 0x1000 - (ofs & 0xfff);
- if (chunk > remain)
- chunk = remain;
- }
- bdl[2] = cpu_to_le32(chunk);
- /* program the IOC to enable interrupt
- * only when the whole fragment is processed
- */
- size -= chunk;
- bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
- bdl += 4;
- azx_dev->frags++;
- ofs += chunk;
- }
- *bdlp = bdl;
- return ofs;
-}
-
-/*
- * set up BDL entries
- */
-static int azx_setup_periods(struct azx *chip,
- struct snd_pcm_substream *substream,
- struct azx_dev *azx_dev)
-{
- u32 *bdl;
- int i, ofs, periods, period_bytes;
- int pos_adj = 0;
-
- /* reset BDL address */
- azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
- azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
-
- period_bytes = azx_dev->period_bytes;
- periods = azx_dev->bufsize / period_bytes;
-
- /* program the initial BDL entries */
- bdl = (u32 *)azx_dev->bdl.area;
- ofs = 0;
- azx_dev->frags = 0;
-
- if (chip->bdl_pos_adj)
- pos_adj = chip->bdl_pos_adj[chip->dev_index];
- if (!azx_dev->no_period_wakeup && pos_adj > 0) {
- struct snd_pcm_runtime *runtime = substream->runtime;
- int pos_align = pos_adj;
- pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
- if (!pos_adj)
- pos_adj = pos_align;
- else
- pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
- pos_align;
- pos_adj = frames_to_bytes(runtime, pos_adj);
- if (pos_adj >= period_bytes) {
- dev_warn(chip->card->dev,"Too big adjustment %d\n",
- pos_adj);
- pos_adj = 0;
- } else {
- ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
- azx_dev,
- &bdl, ofs, pos_adj, true);
- if (ofs < 0)
- goto error;
- }
- } else
- pos_adj = 0;
-
- for (i = 0; i < periods; i++) {
- if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
- period_bytes - pos_adj, 0);
- else
- ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
- period_bytes,
- !azx_dev->no_period_wakeup);
- if (ofs < 0)
- goto error;
- }
- return 0;
-
- error:
- dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n",
- azx_dev->bufsize, period_bytes);
- return -EINVAL;
-}
-
-/*
* PCM ops
*/
@@ -407,13 +94,9 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev = get_azx_dev(substream);
- unsigned long flags;
+ trace_azx_pcm_close(chip, azx_dev);
mutex_lock(&chip->open_mutex);
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = NULL;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
azx_release_device(azx_dev);
if (hinfo->ops.close)
hinfo->ops.close(hinfo, apcm->codec, substream);
@@ -428,18 +111,23 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
int ret;
- dsp_lock(get_azx_dev(substream));
- if (dsp_is_locked(get_azx_dev(substream))) {
+ trace_azx_pcm_hw_params(chip, azx_dev);
+ dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev)) {
ret = -EBUSY;
goto unlock;
}
+ azx_dev->core.bufsize = 0;
+ azx_dev->core.period_bytes = 0;
+ azx_dev->core.format_val = 0;
ret = chip->ops->substream_alloc_pages(chip, substream,
params_buffer_bytes(hw_params));
unlock:
- dsp_unlock(get_azx_dev(substream));
+ dsp_unlock(azx_dev);
return ret;
}
@@ -453,19 +141,13 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
/* reset BDL address */
dsp_lock(azx_dev);
- if (!dsp_is_locked(azx_dev)) {
- azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
- azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
- azx_sd_writel(chip, azx_dev, SD_CTL, 0);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
- }
+ if (!dsp_is_locked(azx_dev))
+ snd_hdac_stream_cleanup(azx_stream(azx_dev));
snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
err = chip->ops->substream_free_pages(chip, substream);
- azx_dev->prepared = 0;
+ azx_stream(azx_dev)->prepared = 0;
dsp_unlock(azx_dev);
return err;
}
@@ -477,21 +159,21 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int bufsize, period_bytes, format_val, stream_tag;
+ unsigned int format_val, stream_tag;
int err;
struct hda_spdif_out *spdif =
snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
unsigned short ctls = spdif ? spdif->ctls : 0;
+ trace_azx_pcm_prepare(chip, azx_dev);
dsp_lock(azx_dev);
if (dsp_is_locked(azx_dev)) {
err = -EBUSY;
goto unlock;
}
- azx_stream_reset(chip, azx_dev);
- format_val = snd_hda_calc_stream_format(apcm->codec,
- runtime->rate,
+ snd_hdac_stream_reset(azx_stream(azx_dev));
+ format_val = snd_hdac_calc_stream_format(runtime->rate,
runtime->channels,
runtime->format,
hinfo->maxbps,
@@ -504,55 +186,23 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
goto unlock;
}
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
-
- dev_dbg(chip->card->dev, "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
- bufsize, format_val);
-
- if (bufsize != azx_dev->bufsize ||
- period_bytes != azx_dev->period_bytes ||
- format_val != azx_dev->format_val ||
- runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
- azx_dev->bufsize = bufsize;
- azx_dev->period_bytes = period_bytes;
- azx_dev->format_val = format_val;
- azx_dev->no_period_wakeup = runtime->no_period_wakeup;
- err = azx_setup_periods(chip, substream, azx_dev);
- if (err < 0)
- goto unlock;
- }
+ err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val);
+ if (err < 0)
+ goto unlock;
- /* when LPIB delay correction gives a small negative value,
- * we ignore it; currently set the threshold statically to
- * 64 frames
- */
- if (runtime->period_size > 64)
- azx_dev->delay_negative_threshold = -frames_to_bytes(runtime, 64);
- else
- azx_dev->delay_negative_threshold = 0;
-
- /* wallclk has 24Mhz clock source */
- azx_dev->period_wallclk = (((runtime->period_size * 24000) /
- runtime->rate) * 1000);
- azx_setup_controller(chip, azx_dev);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- azx_dev->fifo_size =
- azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1;
- else
- azx_dev->fifo_size = 0;
+ snd_hdac_stream_setup(azx_stream(azx_dev));
- stream_tag = azx_dev->stream_tag;
+ stream_tag = azx_dev->core.stream_tag;
/* CA-IBG chips need the playback stream starting from 1 */
if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
stream_tag > chip->capture_streams)
stream_tag -= chip->capture_streams;
err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
- azx_dev->format_val, substream);
+ azx_dev->core.format_val, substream);
unlock:
if (!err)
- azx_dev->prepared = 1;
+ azx_stream(azx_dev)->prepared = 1;
dsp_unlock(azx_dev);
return err;
}
@@ -561,28 +211,36 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
+ struct hdac_bus *bus = azx_bus(chip);
struct azx_dev *azx_dev;
struct snd_pcm_substream *s;
- int rstart = 0, start, nsync = 0, sbits = 0;
- int nwait, timeout;
+ struct hdac_stream *hstr;
+ bool start;
+ int sbits = 0;
+ int sync_reg;
azx_dev = get_azx_dev(substream);
trace_azx_pcm_trigger(chip, azx_dev, cmd);
- if (dsp_is_locked(azx_dev) || !azx_dev->prepared)
+ hstr = azx_stream(azx_dev);
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ sync_reg = AZX_REG_OLD_SSYNC;
+ else
+ sync_reg = AZX_REG_SSYNC;
+
+ if (dsp_is_locked(azx_dev) || !hstr->prepared)
return -EPIPE;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- rstart = 1;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- start = 1;
+ start = true;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- start = 0;
+ start = false;
break;
default:
return -EINVAL;
@@ -592,115 +250,55 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (s->pcm->card != substream->pcm->card)
continue;
azx_dev = get_azx_dev(s);
- sbits |= 1 << azx_dev->index;
- nsync++;
+ sbits |= 1 << azx_dev->core.index;
snd_pcm_trigger_done(s, substream);
}
- spin_lock(&chip->reg_lock);
+ spin_lock(&bus->reg_lock);
/* first, set SYNC bits of corresponding streams */
- if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
- azx_writel(chip, OLD_SSYNC,
- azx_readl(chip, OLD_SSYNC) | sbits);
- else
- azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits);
+ snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg);
snd_pcm_group_for_each_entry(s, substream) {
if (s->pcm->card != substream->pcm->card)
continue;
azx_dev = get_azx_dev(s);
if (start) {
- azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
- if (!rstart)
- azx_dev->start_wallclk -=
- azx_dev->period_wallclk;
- azx_stream_start(chip, azx_dev);
+ azx_dev->insufficient = 1;
+ snd_hdac_stream_start(azx_stream(azx_dev), true);
} else {
- azx_stream_stop(chip, azx_dev);
- }
- azx_dev->running = start;
- }
- spin_unlock(&chip->reg_lock);
- if (start) {
- /* wait until all FIFOs get ready */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (!(azx_sd_readb(chip, azx_dev, SD_STS) &
- SD_STS_FIFO_READY))
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- } else {
- /* wait until all RUN bits are cleared */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (azx_sd_readb(chip, azx_dev, SD_CTL) &
- SD_CTL_DMA_START)
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
+ snd_hdac_stream_stop(azx_stream(azx_dev));
}
}
- spin_lock(&chip->reg_lock);
+ spin_unlock(&bus->reg_lock);
+
+ snd_hdac_stream_sync(hstr, start, sbits);
+
+ spin_lock(&bus->reg_lock);
/* reset SYNC bits */
- if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
- azx_writel(chip, OLD_SSYNC,
- azx_readl(chip, OLD_SSYNC) & ~sbits);
- else
- azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
- if (start) {
- azx_timecounter_init(substream, 0, 0);
- snd_pcm_gettime(substream->runtime, &substream->runtime->trigger_tstamp);
- substream->runtime->trigger_tstamp_latched = true;
-
- if (nsync > 1) {
- cycle_t cycle_last;
-
- /* same start cycle for master and group */
- azx_dev = get_azx_dev(substream);
- cycle_last = azx_dev->azx_tc.cycle_last;
-
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_timecounter_init(s, 1, cycle_last);
- }
- }
- }
- spin_unlock(&chip->reg_lock);
+ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg);
+ if (start)
+ snd_hdac_stream_timecounter_init(hstr, sbits);
+ spin_unlock(&bus->reg_lock);
return 0;
}
unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev)
{
- return azx_sd_readl(chip, azx_dev, SD_LPIB);
+ return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
}
EXPORT_SYMBOL_GPL(azx_get_pos_lpib);
unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev)
{
- return le32_to_cpu(*azx_dev->posbuf);
+ return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev));
}
EXPORT_SYMBOL_GPL(azx_get_pos_posbuf);
unsigned int azx_get_position(struct azx *chip,
struct azx_dev *azx_dev)
{
- struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
unsigned int pos;
int stream = substream->stream;
int delay = 0;
@@ -710,7 +308,7 @@ unsigned int azx_get_position(struct azx *chip,
else /* use the position buffer as default */
pos = azx_get_pos_posbuf(chip, azx_dev);
- if (pos >= azx_dev->bufsize)
+ if (pos >= azx_dev->core.bufsize)
pos = 0;
if (substream->runtime) {
@@ -752,7 +350,7 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
snd_pcm_gettime(substream->runtime, system_ts);
- nsec = timecounter_read(&azx_dev->azx_tc);
+ nsec = timecounter_read(&azx_dev->core.tc);
nsec = div_u64(nsec, 3); /* can be optimized */
if (audio_tstamp_config->report_delay)
nsec = azx_adjust_codec_delay(substream, nsec);
@@ -802,17 +400,18 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
int err;
int buff_step;
snd_hda_codec_pcm_get(apcm->info);
mutex_lock(&chip->open_mutex);
azx_dev = azx_assign_device(chip, substream);
+ trace_azx_pcm_open(chip, azx_dev);
if (azx_dev == NULL) {
err = -EBUSY;
goto unlock;
}
+ runtime->private_data = azx_dev;
runtime->hw = azx_pcm_hw;
runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max;
@@ -874,12 +473,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = substream;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- runtime->private_data = azx_dev;
snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex);
return 0;
@@ -928,10 +521,11 @@ static void azx_pcm_free(struct snd_pcm *pcm)
#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
-static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm)
+int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm)
{
- struct azx *chip = bus->private_data;
+ struct hdac_bus *bus = &_bus->core;
+ struct azx *chip = bus_to_azx(bus);
struct snd_pcm *pcm;
struct azx_pcm *apcm;
int pcm_dev = cpcm->device;
@@ -979,89 +573,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
return 0;
}
-/*
- * CORB / RIRB interface
- */
-static int azx_alloc_cmd_io(struct azx *chip)
-{
- /* single page (at least 4096 bytes) must suffice for both ringbuffes */
- return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
- PAGE_SIZE, &chip->rb);
-}
-
-static void azx_init_cmd_io(struct azx *chip)
-{
- int timeout;
-
- spin_lock_irq(&chip->reg_lock);
- /* CORB set up */
- chip->corb.addr = chip->rb.addr;
- chip->corb.buf = (u32 *)chip->rb.area;
- azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
- azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
-
- /* set the corb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, CORBSIZE, 0x02);
- /* set the corb write pointer to 0 */
- azx_writew(chip, CORBWP, 0);
-
- /* reset the corb hw read pointer */
- azx_writew(chip, CORBRP, AZX_CORBRP_RST);
- if (!(chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)) {
- for (timeout = 1000; timeout > 0; timeout--) {
- if ((azx_readw(chip, CORBRP) & AZX_CORBRP_RST) == AZX_CORBRP_RST)
- break;
- udelay(1);
- }
- if (timeout <= 0)
- dev_err(chip->card->dev, "CORB reset timeout#1, CORBRP = %d\n",
- azx_readw(chip, CORBRP));
-
- azx_writew(chip, CORBRP, 0);
- for (timeout = 1000; timeout > 0; timeout--) {
- if (azx_readw(chip, CORBRP) == 0)
- break;
- udelay(1);
- }
- if (timeout <= 0)
- dev_err(chip->card->dev, "CORB reset timeout#2, CORBRP = %d\n",
- azx_readw(chip, CORBRP));
- }
-
- /* enable corb dma */
- azx_writeb(chip, CORBCTL, AZX_CORBCTL_RUN);
-
- /* RIRB set up */
- chip->rirb.addr = chip->rb.addr + 2048;
- chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
- chip->rirb.wp = chip->rirb.rp = 0;
- memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
- azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
- azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
-
- /* set the rirb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, RIRBSIZE, 0x02);
- /* reset the rirb hw write pointer */
- azx_writew(chip, RIRBWP, AZX_RIRBWP_RST);
- /* set N=1, get RIRB response interrupt for new entry */
- if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
- azx_writew(chip, RINTCNT, 0xc0);
- else
- azx_writew(chip, RINTCNT, 1);
- /* enable rirb dma and response irq */
- azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static void azx_free_cmd_io(struct azx *chip)
-{
- spin_lock_irq(&chip->reg_lock);
- /* disable ringbuffer DMAs */
- azx_writeb(chip, RIRBCTL, 0);
- azx_writeb(chip, CORBCTL, 0);
- spin_unlock_irq(&chip->reg_lock);
-}
-
static unsigned int azx_command_addr(u32 cmd)
{
unsigned int addr = cmd >> 28;
@@ -1074,92 +585,12 @@ static unsigned int azx_command_addr(u32 cmd)
return addr;
}
-/* send a command */
-static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
-{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- unsigned int wp, rp;
-
- spin_lock_irq(&chip->reg_lock);
-
- /* add command to corb */
- wp = azx_readw(chip, CORBWP);
- if (wp == 0xffff) {
- /* something wrong, controller likely turned to D3 */
- spin_unlock_irq(&chip->reg_lock);
- return -EIO;
- }
- wp++;
- wp %= AZX_MAX_CORB_ENTRIES;
-
- rp = azx_readw(chip, CORBRP);
- if (wp == rp) {
- /* oops, it's full */
- spin_unlock_irq(&chip->reg_lock);
- return -EAGAIN;
- }
-
- chip->rirb.cmds[addr]++;
- chip->corb.buf[wp] = cpu_to_le32(val);
- azx_writew(chip, CORBWP, wp);
-
- spin_unlock_irq(&chip->reg_lock);
-
- return 0;
-}
-
-#define AZX_RIRB_EX_UNSOL_EV (1<<4)
-
-/* retrieve RIRB entry - called from interrupt handler */
-static void azx_update_rirb(struct azx *chip)
-{
- unsigned int rp, wp;
- unsigned int addr;
- u32 res, res_ex;
-
- wp = azx_readw(chip, RIRBWP);
- if (wp == 0xffff) {
- /* something wrong, controller likely turned to D3 */
- return;
- }
-
- if (wp == chip->rirb.wp)
- return;
- chip->rirb.wp = wp;
-
- while (chip->rirb.rp != wp) {
- chip->rirb.rp++;
- chip->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
-
- rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
- res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
- res = le32_to_cpu(chip->rirb.buf[rp]);
- addr = res_ex & 0xf;
- if ((addr >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) {
- dev_err(chip->card->dev, "spurious response %#x:%#x, rp = %d, wp = %d",
- res, res_ex,
- chip->rirb.rp, wp);
- snd_BUG();
- } else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
- snd_hda_queue_unsol_event(chip->bus, res, res_ex);
- else if (chip->rirb.cmds[addr]) {
- chip->rirb.res[addr] = res;
- smp_wmb();
- chip->rirb.cmds[addr]--;
- } else if (printk_ratelimit()) {
- dev_err(chip->card->dev, "spurious response %#x:%#x, last cmd=%#08x\n",
- res, res_ex,
- chip->last_cmd[addr]);
- }
- }
-}
-
/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus,
- unsigned int addr)
+static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
{
- struct azx *chip = bus->private_data;
+ struct azx *chip = bus_to_azx(bus);
+ struct hda_bus *hbus = &chip->bus;
unsigned long timeout;
unsigned long loopcounter;
int do_poll = 0;
@@ -1168,22 +599,21 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
timeout = jiffies + msecs_to_jiffies(1000);
for (loopcounter = 0;; loopcounter++) {
- if (chip->polling_mode || do_poll) {
- spin_lock_irq(&chip->reg_lock);
- azx_update_rirb(chip);
- spin_unlock_irq(&chip->reg_lock);
- }
- if (!chip->rirb.cmds[addr]) {
- smp_rmb();
- bus->rirb_error = 0;
-
+ spin_lock_irq(&bus->reg_lock);
+ if (chip->polling_mode || do_poll)
+ snd_hdac_bus_update_rirb(bus);
+ if (!bus->rirb.cmds[addr]) {
if (!do_poll)
chip->poll_count = 0;
- return chip->rirb.res[addr]; /* the last value */
+ if (res)
+ *res = bus->rirb.res[addr]; /* the last value */
+ spin_unlock_irq(&bus->reg_lock);
+ return 0;
}
+ spin_unlock_irq(&bus->reg_lock);
if (time_after(jiffies, timeout))
break;
- if (bus->needs_damn_long_delay || loopcounter > 3000)
+ if (hbus->needs_damn_long_delay || loopcounter > 3000)
msleep(2); /* temporary workaround */
else {
udelay(10);
@@ -1191,13 +621,13 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
}
}
- if (bus->no_response_fallback)
- return -1;
+ if (hbus->no_response_fallback)
+ return -EIO;
if (!chip->polling_mode && chip->poll_count < 2) {
dev_dbg(chip->card->dev,
"azx_get_response timeout, polling the codec once: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
+ bus->last_cmd[addr]);
do_poll = 1;
chip->poll_count++;
goto again;
@@ -1207,7 +637,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
if (!chip->polling_mode) {
dev_warn(chip->card->dev,
"azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
+ bus->last_cmd[addr]);
chip->polling_mode = 1;
goto again;
}
@@ -1215,12 +645,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
if (chip->msi) {
dev_warn(chip->card->dev,
"No response from codec, disabling MSI: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- if (chip->ops->disable_msi_reset_irq(chip) &&
- chip->ops->disable_msi_reset_irq(chip) < 0) {
- bus->rirb_error = 1;
- return -1;
- }
+ bus->last_cmd[addr]);
+ if (chip->ops->disable_msi_reset_irq &&
+ chip->ops->disable_msi_reset_irq(chip) < 0)
+ return -EIO;
goto again;
}
@@ -1229,28 +657,24 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
* phase, this is likely an access to a non-existing codec
* slot. Better to return an error and reset the system.
*/
- return -1;
+ return -EIO;
}
/* a fatal communication error; need either to reset or to fallback
* to the single_cmd mode
*/
- bus->rirb_error = 1;
- if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
- bus->response_reset = 1;
- return -1; /* give a chance to retry */
+ if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
+ hbus->response_reset = 1;
+ return -EAGAIN; /* give a chance to retry */
}
dev_err(chip->card->dev,
"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
+ bus->last_cmd[addr]);
chip->single_cmd = 1;
- bus->response_reset = 0;
- /* release CORB/RIRB */
- azx_free_cmd_io(chip);
- /* disable unsolicited responses */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL);
- return -1;
+ hbus->response_reset = 0;
+ snd_hdac_bus_stop_cmd_io(bus);
+ return -EIO;
}
/*
@@ -1272,7 +696,7 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
/* check IRV busy bit */
if (azx_readw(chip, IRS) & AZX_IRS_VALID) {
/* reuse rirb.res as the response return value */
- chip->rirb.res[addr] = azx_readl(chip, IR);
+ azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR);
return 0;
}
udelay(1);
@@ -1280,18 +704,18 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
if (printk_ratelimit())
dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n",
azx_readw(chip, IRS));
- chip->rirb.res[addr] = -1;
+ azx_bus(chip)->rirb.res[addr] = -1;
return -EIO;
}
/* send a command */
-static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
+static int azx_single_send_cmd(struct hdac_bus *bus, u32 val)
{
- struct azx *chip = bus->private_data;
+ struct azx *chip = bus_to_azx(bus);
unsigned int addr = azx_command_addr(val);
int timeout = 50;
- bus->rirb_error = 0;
+ bus->last_cmd[azx_command_addr(val)] = val;
while (timeout--) {
/* check ICB busy bit */
if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) {
@@ -1313,11 +737,12 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
}
/* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus,
- unsigned int addr)
+static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
{
- struct azx *chip = bus->private_data;
- return chip->rirb.res[addr];
+ if (res)
+ *res = bus->rirb.res[addr];
+ return 0;
}
/*
@@ -1328,32 +753,48 @@ static unsigned int azx_single_get_response(struct hda_bus *bus,
*/
/* send a command */
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
+static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
{
- struct azx *chip = bus->private_data;
+ struct azx *chip = bus_to_azx(bus);
if (chip->disabled)
return 0;
- chip->last_cmd[azx_command_addr(val)] = val;
if (chip->single_cmd)
return azx_single_send_cmd(bus, val);
else
- return azx_corb_send_cmd(bus, val);
+ return snd_hdac_bus_send_cmd(bus, val);
}
/* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus,
- unsigned int addr)
+static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
{
- struct azx *chip = bus->private_data;
+ struct azx *chip = bus_to_azx(bus);
+
if (chip->disabled)
return 0;
if (chip->single_cmd)
- return azx_single_get_response(bus, addr);
+ return azx_single_get_response(bus, addr, res);
+ else
+ return azx_rirb_get_response(bus, addr, res);
+}
+
+static int azx_link_power(struct hdac_bus *bus, bool enable)
+{
+ struct azx *chip = bus_to_azx(bus);
+
+ if (chip->ops->link_power)
+ return chip->ops->link_power(chip, enable);
else
- return azx_rirb_get_response(bus, addr);
+ return -EINVAL;
}
+static const struct hdac_bus_ops bus_core_ops = {
+ .command = azx_send_cmd,
+ .get_response = azx_get_response,
+ .link_power = azx_link_power,
+};
+
#ifdef CONFIG_SND_HDA_DSP_LOADER
/*
* DSP loading code (e.g. for CA0132)
@@ -1363,339 +804,132 @@ static unsigned int azx_get_response(struct hda_bus *bus,
static struct azx_dev *
azx_get_dsp_loader_dev(struct azx *chip)
{
- return &chip->azx_dev[chip->playback_index_offset];
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list)
+ if (s->index == chip->playback_index_offset)
+ return stream_to_azx_dev(s);
+
+ return NULL;
}
-static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
- unsigned int byte_size,
- struct snd_dma_buffer *bufp)
+int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp)
{
- u32 *bdl;
- struct azx *chip = bus->private_data;
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
struct azx_dev *azx_dev;
+ struct hdac_stream *hstr;
+ bool saved = false;
int err;
azx_dev = azx_get_dsp_loader_dev(chip);
-
- dsp_lock(azx_dev);
- spin_lock_irq(&chip->reg_lock);
- if (azx_dev->running || azx_dev->locked) {
- spin_unlock_irq(&chip->reg_lock);
- err = -EBUSY;
- goto unlock;
+ hstr = azx_stream(azx_dev);
+ spin_lock_irq(&bus->reg_lock);
+ if (hstr->opened) {
+ chip->saved_azx_dev = *azx_dev;
+ saved = true;
}
- azx_dev->prepared = 0;
- chip->saved_azx_dev = *azx_dev;
- azx_dev->locked = 1;
- spin_unlock_irq(&chip->reg_lock);
-
- err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV_SG,
- byte_size, bufp);
- if (err < 0)
- goto err_alloc;
+ spin_unlock_irq(&bus->reg_lock);
- azx_dev->bufsize = byte_size;
- azx_dev->period_bytes = byte_size;
- azx_dev->format_val = format;
-
- azx_stream_reset(chip, azx_dev);
-
- /* reset BDL address */
- azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
- azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
-
- azx_dev->frags = 0;
- bdl = (u32 *)azx_dev->bdl.area;
- err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0);
- if (err < 0)
- goto error;
-
- azx_setup_controller(chip, azx_dev);
- dsp_unlock(azx_dev);
- return azx_dev->stream_tag;
+ err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp);
+ if (err < 0) {
+ spin_lock_irq(&bus->reg_lock);
+ if (saved)
+ *azx_dev = chip->saved_azx_dev;
+ spin_unlock_irq(&bus->reg_lock);
+ return err;
+ }
- error:
- chip->ops->dma_free_pages(chip, bufp);
- err_alloc:
- spin_lock_irq(&chip->reg_lock);
- if (azx_dev->opened)
- *azx_dev = chip->saved_azx_dev;
- azx_dev->locked = 0;
- spin_unlock_irq(&chip->reg_lock);
- unlock:
- dsp_unlock(azx_dev);
+ hstr->prepared = 0;
return err;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare);
-static void azx_load_dsp_trigger(struct hda_bus *bus, bool start)
+void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
{
- struct azx *chip = bus->private_data;
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
- if (start)
- azx_stream_start(chip, azx_dev);
- else
- azx_stream_stop(chip, azx_dev);
- azx_dev->running = start;
+ snd_hdac_dsp_trigger(azx_stream(azx_dev), start);
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger);
-static void azx_load_dsp_cleanup(struct hda_bus *bus,
- struct snd_dma_buffer *dmab)
+void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+ struct snd_dma_buffer *dmab)
{
- struct azx *chip = bus->private_data;
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+ struct hdac_stream *hstr = azx_stream(azx_dev);
- if (!dmab->area || !azx_dev->locked)
+ if (!dmab->area || !hstr->locked)
return;
- dsp_lock(azx_dev);
- /* reset BDL address */
- azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
- azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
- azx_sd_writel(chip, azx_dev, SD_CTL, 0);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
-
- chip->ops->dma_free_pages(chip, dmab);
- dmab->area = NULL;
-
- spin_lock_irq(&chip->reg_lock);
- if (azx_dev->opened)
+ snd_hdac_dsp_cleanup(hstr, dmab);
+ spin_lock_irq(&bus->reg_lock);
+ if (hstr->opened)
*azx_dev = chip->saved_azx_dev;
- azx_dev->locked = 0;
- spin_unlock_irq(&chip->reg_lock);
- dsp_unlock(azx_dev);
+ hstr->locked = false;
+ spin_unlock_irq(&bus->reg_lock);
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup);
#endif /* CONFIG_SND_HDA_DSP_LOADER */
-int azx_alloc_stream_pages(struct azx *chip)
-{
- int i, err;
-
- for (i = 0; i < chip->num_streams; i++) {
- dsp_lock_init(&chip->azx_dev[i]);
- /* allocate memory for the BDL for each stream */
- err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
- BDL_SIZE,
- &chip->azx_dev[i].bdl);
- if (err < 0)
- return -ENOMEM;
- }
- /* allocate memory for the position buffer */
- err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
- chip->num_streams * 8, &chip->posbuf);
- if (err < 0)
- return -ENOMEM;
-
- /* allocate CORB/RIRB */
- err = azx_alloc_cmd_io(chip);
- if (err < 0)
- return err;
- return 0;
-}
-EXPORT_SYMBOL_GPL(azx_alloc_stream_pages);
-
-void azx_free_stream_pages(struct azx *chip)
-{
- int i;
- if (chip->azx_dev) {
- for (i = 0; i < chip->num_streams; i++)
- if (chip->azx_dev[i].bdl.area)
- chip->ops->dma_free_pages(
- chip, &chip->azx_dev[i].bdl);
- }
- if (chip->rb.area)
- chip->ops->dma_free_pages(chip, &chip->rb);
- if (chip->posbuf.area)
- chip->ops->dma_free_pages(chip, &chip->posbuf);
-}
-EXPORT_SYMBOL_GPL(azx_free_stream_pages);
-
/*
- * Lowlevel interface
+ * reset and start the controller registers
*/
-
-/* enter link reset */
-void azx_enter_link_reset(struct azx *chip)
-{
- unsigned long timeout;
-
- /* reset controller */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_RESET);
-
- timeout = jiffies + msecs_to_jiffies(100);
- while ((azx_readb(chip, GCTL) & AZX_GCTL_RESET) &&
- time_before(jiffies, timeout))
- usleep_range(500, 1000);
-}
-EXPORT_SYMBOL_GPL(azx_enter_link_reset);
-
-/* exit link reset */
-static void azx_exit_link_reset(struct azx *chip)
-{
- unsigned long timeout;
-
- azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | AZX_GCTL_RESET);
-
- timeout = jiffies + msecs_to_jiffies(100);
- while (!azx_readb(chip, GCTL) &&
- time_before(jiffies, timeout))
- usleep_range(500, 1000);
-}
-
-/* reset codec link */
-static int azx_reset(struct azx *chip, bool full_reset)
-{
- if (!full_reset)
- goto __skip;
-
- /* clear STATESTS */
- azx_writew(chip, STATESTS, STATESTS_INT_MASK);
-
- /* reset controller */
- azx_enter_link_reset(chip);
-
- /* delay for >= 100us for codec PLL to settle per spec
- * Rev 0.9 section 5.5.1
- */
- usleep_range(500, 1000);
-
- /* Bring controller out of reset */
- azx_exit_link_reset(chip);
-
- /* Brent Chartrand said to wait >= 540us for codecs to initialize */
- usleep_range(1000, 1200);
-
- __skip:
- /* check to see if controller is ready */
- if (!azx_readb(chip, GCTL)) {
- dev_dbg(chip->card->dev, "azx_reset: controller not ready!\n");
- return -EBUSY;
- }
-
- /* Accept unsolicited responses */
- if (!chip->single_cmd)
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
- AZX_GCTL_UNSOL);
-
- /* detect codecs */
- if (!chip->codec_mask) {
- chip->codec_mask = azx_readw(chip, STATESTS);
- dev_dbg(chip->card->dev, "codec_mask = 0x%x\n",
- chip->codec_mask);
- }
-
- return 0;
-}
-
-/* enable interrupts */
-static void azx_int_enable(struct azx *chip)
-{
- /* enable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
- AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
-}
-
-/* disable interrupts */
-static void azx_int_disable(struct azx *chip)
-{
- int i;
-
- /* disable interrupts in stream descriptor */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(chip, azx_dev, SD_CTL,
- azx_sd_readb(chip, azx_dev, SD_CTL) &
- ~SD_INT_MASK);
- }
-
- /* disable SIE for all streams */
- azx_writeb(chip, INTCTL, 0);
-
- /* disable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
- ~(AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN));
-}
-
-/* clear interrupts */
-static void azx_int_clear(struct azx *chip)
+void azx_init_chip(struct azx *chip, bool full_reset)
{
- int i;
-
- /* clear stream status */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK);
+ if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) {
+ /* correct RINTCNT for CXT */
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+ azx_writew(chip, RINTCNT, 0xc0);
}
-
- /* clear STATESTS */
- azx_writew(chip, STATESTS, STATESTS_INT_MASK);
-
- /* clear rirb status */
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
-
- /* clear int status */
- azx_writel(chip, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
}
+EXPORT_SYMBOL_GPL(azx_init_chip);
-/*
- * reset and start the controller registers
- */
-void azx_init_chip(struct azx *chip, bool full_reset)
+void azx_stop_all_streams(struct azx *chip)
{
- if (chip->initialized)
- return;
-
- /* reset controller */
- azx_reset(chip, full_reset);
-
- /* initialize interrupts */
- azx_int_clear(chip);
- azx_int_enable(chip);
-
- /* initialize the codec command I/O */
- if (!chip->single_cmd)
- azx_init_cmd_io(chip);
-
- /* program the position buffer */
- azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
- azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
- chip->initialized = 1;
+ list_for_each_entry(s, &bus->stream_list, list)
+ snd_hdac_stream_stop(s);
}
-EXPORT_SYMBOL_GPL(azx_init_chip);
+EXPORT_SYMBOL_GPL(azx_stop_all_streams);
void azx_stop_chip(struct azx *chip)
{
- if (!chip->initialized)
- return;
-
- /* disable interrupts */
- azx_int_disable(chip);
- azx_int_clear(chip);
-
- /* disable CORB/RIRB */
- azx_free_cmd_io(chip);
-
- /* disable position buffer */
- azx_writel(chip, DPLBASE, 0);
- azx_writel(chip, DPUBASE, 0);
-
- chip->initialized = 0;
+ snd_hdac_bus_stop_chip(azx_bus(chip));
}
EXPORT_SYMBOL_GPL(azx_stop_chip);
/*
* interrupt handler
*/
+static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
+{
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+
+ /* check whether this IRQ is really acceptable */
+ if (!chip->ops->position_check ||
+ chip->ops->position_check(chip, azx_dev)) {
+ spin_unlock(&bus->reg_lock);
+ snd_pcm_period_elapsed(azx_stream(azx_dev)->substream);
+ spin_lock(&bus->reg_lock);
+ }
+}
+
irqreturn_t azx_interrupt(int irq, void *dev_id)
{
struct azx *chip = dev_id;
- struct azx_dev *azx_dev;
+ struct hdac_bus *bus = azx_bus(chip);
u32 status;
- u8 sd_status;
- int i;
#ifdef CONFIG_PM
if (azx_has_pm_runtime(chip))
@@ -1703,36 +937,20 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
return IRQ_NONE;
#endif
- spin_lock(&chip->reg_lock);
+ spin_lock(&bus->reg_lock);
if (chip->disabled) {
- spin_unlock(&chip->reg_lock);
+ spin_unlock(&bus->reg_lock);
return IRQ_NONE;
}
status = azx_readl(chip, INTSTS);
if (status == 0 || status == 0xffffffff) {
- spin_unlock(&chip->reg_lock);
+ spin_unlock(&bus->reg_lock);
return IRQ_NONE;
}
- for (i = 0; i < chip->num_streams; i++) {
- azx_dev = &chip->azx_dev[i];
- if (status & azx_dev->sd_int_sta_mask) {
- sd_status = azx_sd_readb(chip, azx_dev, SD_STS);
- azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK);
- if (!azx_dev->substream || !azx_dev->running ||
- !(sd_status & SD_INT_COMPLETE))
- continue;
- /* check whether this IRQ is really acceptable */
- if (!chip->ops->position_check ||
- chip->ops->position_check(chip, azx_dev)) {
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
- }
- }
- }
+ snd_hdac_bus_handle_stream_irq(bus, status, stream_update);
/* clear rirb int */
status = azx_readb(chip, RIRBSTS);
@@ -1740,12 +958,12 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
if (status & RIRB_INT_RESPONSE) {
if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
udelay(80);
- azx_update_rirb(chip);
+ snd_hdac_bus_update_rirb(bus);
}
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
- spin_unlock(&chip->reg_lock);
+ spin_unlock(&bus->reg_lock);
return IRQ_HANDLED;
}
@@ -1762,29 +980,31 @@ static int probe_codec(struct azx *chip, int addr)
{
unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+ unsigned int res = -1;
- mutex_lock(&chip->bus->core.cmd_mutex);
+ mutex_lock(&bus->cmd_mutex);
chip->probing = 1;
- azx_send_cmd(chip->bus, cmd);
- res = azx_get_response(chip->bus, addr);
+ azx_send_cmd(bus, cmd);
+ err = azx_get_response(bus, addr, &res);
chip->probing = 0;
- mutex_unlock(&chip->bus->core.cmd_mutex);
- if (res == -1)
+ mutex_unlock(&bus->cmd_mutex);
+ if (err < 0 || res == -1)
return -EIO;
dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
return 0;
}
-static void azx_bus_reset(struct hda_bus *bus)
+void snd_hda_bus_reset(struct hda_bus *bus)
{
- struct azx *chip = bus->private_data;
+ struct azx *chip = bus_to_azx(&bus->core);
bus->in_reset = 1;
azx_stop_chip(chip);
azx_init_chip(chip, true);
- if (chip->initialized)
- snd_hda_bus_reset(chip->bus);
+ if (bus->core.chip_init)
+ snd_hda_bus_reset_codecs(bus);
bus->in_reset = 0;
}
@@ -1809,33 +1029,30 @@ static int get_jackpoll_interval(struct azx *chip)
return j;
}
-static struct hda_bus_ops bus_ops = {
- .command = azx_send_cmd,
- .get_response = azx_get_response,
- .attach_pcm = azx_attach_pcm_stream,
- .bus_reset = azx_bus_reset,
-#ifdef CONFIG_SND_HDA_DSP_LOADER
- .load_dsp_prepare = azx_load_dsp_prepare,
- .load_dsp_trigger = azx_load_dsp_trigger,
- .load_dsp_cleanup = azx_load_dsp_cleanup,
-#endif
-};
-
/* HD-audio bus initialization */
-int azx_bus_create(struct azx *chip, const char *model)
+int azx_bus_init(struct azx *chip, const char *model,
+ const struct hdac_io_ops *io_ops)
{
- struct hda_bus *bus;
+ struct hda_bus *bus = &chip->bus;
int err;
- err = snd_hda_bus_new(chip->card, &bus);
+ err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops,
+ io_ops);
if (err < 0)
return err;
- chip->bus = bus;
- bus->private_data = chip;
+ bus->card = chip->card;
+ mutex_init(&bus->prepare_mutex);
bus->pci = chip->pci;
bus->modelname = model;
- bus->ops = bus_ops;
+ bus->core.snoop = azx_snoop(chip);
+ if (chip->get_position[0] != azx_get_pos_lpib ||
+ chip->get_position[1] != azx_get_pos_lpib)
+ bus->core.use_posbuf = true;
+ if (chip->bdl_pos_adj)
+ bus->core.bdl_pos_adj = chip->bdl_pos_adj[chip->dev_index];
+ if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
+ bus->core.corbrp_self_clear = true;
if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
@@ -1854,12 +1071,12 @@ int azx_bus_create(struct azx *chip, const char *model)
return 0;
}
-EXPORT_SYMBOL_GPL(azx_bus_create);
+EXPORT_SYMBOL_GPL(azx_bus_init);
/* Probe codecs */
int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
{
- struct hda_bus *bus = chip->bus;
+ struct hdac_bus *bus = azx_bus(chip);
int c, codecs, err;
codecs = 0;
@@ -1868,14 +1085,14 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
/* First try to probe all given codec slots */
for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
if (probe_codec(chip, c) < 0) {
/* Some BIOSen give you wrong codec addresses
* that don't exist
*/
dev_warn(chip->card->dev,
"Codec #%d probe error; disabling it...\n", c);
- chip->codec_mask &= ~(1 << c);
+ bus->codec_mask &= ~(1 << c);
/* More badly, accessing to a non-existing
* codec often screws up the controller chip,
* and disturbs the further communications.
@@ -1891,9 +1108,9 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
/* Then create codec instances */
for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
struct hda_codec *codec;
- err = snd_hda_codec_new(bus, bus->card, c, &codec);
+ err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
if (err < 0)
continue;
codec->jackpoll_interval = get_jackpoll_interval(chip);
@@ -1913,40 +1130,39 @@ EXPORT_SYMBOL_GPL(azx_probe_codecs);
int azx_codec_configure(struct azx *chip)
{
struct hda_codec *codec;
- list_for_each_codec(codec, chip->bus) {
+ list_for_each_codec(codec, &chip->bus) {
snd_hda_codec_configure(codec);
}
return 0;
}
EXPORT_SYMBOL_GPL(azx_codec_configure);
-
-static bool is_input_stream(struct azx *chip, unsigned char index)
+static int stream_direction(struct azx *chip, unsigned char index)
{
- return (index >= chip->capture_index_offset &&
- index < chip->capture_index_offset + chip->capture_streams);
+ if (index >= chip->capture_index_offset &&
+ index < chip->capture_index_offset + chip->capture_streams)
+ return SNDRV_PCM_STREAM_CAPTURE;
+ return SNDRV_PCM_STREAM_PLAYBACK;
}
/* initialize SD streams */
-int azx_init_stream(struct azx *chip)
+int azx_init_streams(struct azx *chip)
{
int i;
- int in_stream_tag = 0;
- int out_stream_tag = 0;
+ int stream_tags[2] = { 0, 0 };
/* initialize each stream (aka device)
* assign the starting bdl address to each stream (device)
* and initialize
*/
for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
- /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
- azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
- /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
- azx_dev->sd_int_sta_mask = 1 << i;
- azx_dev->index = i;
+ struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL);
+ int dir, tag;
+
+ if (!azx_dev)
+ return -ENOMEM;
+ dir = stream_direction(chip, i);
/* stream tag must be unique throughout
* the stream direction group,
* valid values 1...15
@@ -1954,17 +1170,26 @@ int azx_init_stream(struct azx *chip)
* AZX_DCAPS_SEPARATE_STREAM_TAG is used
*/
if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
- azx_dev->stream_tag =
- is_input_stream(chip, i) ?
- ++in_stream_tag :
- ++out_stream_tag;
+ tag = ++stream_tags[dir];
else
- azx_dev->stream_tag = i + 1;
+ tag = i + 1;
+ snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev),
+ i, dir, tag);
}
return 0;
}
-EXPORT_SYMBOL_GPL(azx_init_stream);
+EXPORT_SYMBOL_GPL(azx_init_streams);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Common HDA driver functions");
+void azx_free_streams(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ while (!list_empty(&bus->stream_list)) {
+ s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
+ list_del(&s->list);
+ kfree(stream_to_azx_dev(s));
+ }
+}
+EXPORT_SYMBOL_GPL(azx_free_streams);
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index 0efdb094d21c..314105cd5061 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -21,135 +21,10 @@
#include <sound/pcm.h>
#include <sound/initval.h>
#include "hda_codec.h"
+#include <sound/hda_register.h>
-/*
- * registers
- */
-#define AZX_REG_GCAP 0x00
-#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */
-#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */
-#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */
-#define AZX_GCAP_ISS (15 << 8) /* # of input streams */
-#define AZX_GCAP_OSS (15 << 12) /* # of output streams */
-#define AZX_REG_VMIN 0x02
-#define AZX_REG_VMAJ 0x03
-#define AZX_REG_OUTPAY 0x04
-#define AZX_REG_INPAY 0x06
-#define AZX_REG_GCTL 0x08
-#define AZX_GCTL_RESET (1 << 0) /* controller reset */
-#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */
-#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
-#define AZX_REG_WAKEEN 0x0c
-#define AZX_REG_STATESTS 0x0e
-#define AZX_REG_GSTS 0x10
-#define AZX_GSTS_FSTS (1 << 1) /* flush status */
-#define AZX_REG_INTCTL 0x20
-#define AZX_REG_INTSTS 0x24
-#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */
-#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
-#define AZX_REG_SSYNC 0x38
-#define AZX_REG_CORBLBASE 0x40
-#define AZX_REG_CORBUBASE 0x44
-#define AZX_REG_CORBWP 0x48
-#define AZX_REG_CORBRP 0x4a
-#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */
-#define AZX_REG_CORBCTL 0x4c
-#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */
-#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
-#define AZX_REG_CORBSTS 0x4d
-#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */
-#define AZX_REG_CORBSIZE 0x4e
-
-#define AZX_REG_RIRBLBASE 0x50
-#define AZX_REG_RIRBUBASE 0x54
-#define AZX_REG_RIRBWP 0x58
-#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */
-#define AZX_REG_RINTCNT 0x5a
-#define AZX_REG_RIRBCTL 0x5c
-#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
-#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */
-#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
-#define AZX_REG_RIRBSTS 0x5d
-#define AZX_RBSTS_IRQ (1 << 0) /* response irq */
-#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */
-#define AZX_REG_RIRBSIZE 0x5e
-
-#define AZX_REG_IC 0x60
-#define AZX_REG_IR 0x64
-#define AZX_REG_IRS 0x68
-#define AZX_IRS_VALID (1<<1)
-#define AZX_IRS_BUSY (1<<0)
-
-#define AZX_REG_DPLBASE 0x70
-#define AZX_REG_DPUBASE 0x74
-#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */
-
-/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
-enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
-
-/* stream register offsets from stream base */
-#define AZX_REG_SD_CTL 0x00
-#define AZX_REG_SD_STS 0x03
-#define AZX_REG_SD_LPIB 0x04
-#define AZX_REG_SD_CBL 0x08
-#define AZX_REG_SD_LVI 0x0c
-#define AZX_REG_SD_FIFOW 0x0e
-#define AZX_REG_SD_FIFOSIZE 0x10
-#define AZX_REG_SD_FORMAT 0x12
-#define AZX_REG_SD_BDLPL 0x18
-#define AZX_REG_SD_BDLPU 0x1c
-
-/* PCI space */
-#define AZX_PCIREG_TCSEL 0x44
-
-/*
- * other constants
- */
-
-/* max number of fragments - we may use more if allocating more pages for BDL */
-#define BDL_SIZE 4096
-#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
-#define AZX_MAX_FRAG 32
-/* max buffer size - no h/w limit, you can increase as you like */
-#define AZX_MAX_BUF_SIZE (1024*1024*1024)
-
-/* RIRB int mask: overrun[2], response[0] */
-#define RIRB_INT_RESPONSE 0x01
-#define RIRB_INT_OVERRUN 0x04
-#define RIRB_INT_MASK 0x05
-
-/* STATESTS int mask: S3,SD2,SD1,SD0 */
-#define AZX_MAX_CODECS 8
+#define AZX_MAX_CODECS HDA_MAX_CODECS
#define AZX_DEFAULT_CODECS 4
-#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
-
-/* SD_CTL bits */
-#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
-#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
-#define SD_CTL_STRIPE (3 << 16) /* stripe control */
-#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
-#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
-#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
-#define SD_CTL_STREAM_TAG_SHIFT 20
-
-/* SD_CTL and SD_STS */
-#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
-#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
-#define SD_INT_COMPLETE 0x04 /* completion interrupt */
-#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
- SD_INT_COMPLETE)
-
-/* SD_STS */
-#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
-
-/* INTCTL and INTSTS */
-#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */
-#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
-#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
-
-/* below are so far hardcoded - should read registers in future */
-#define AZX_MAX_CORB_ENTRIES 256
-#define AZX_MAX_RIRB_ENTRIES 256
/* driver quirks (capabilities) */
/* bits 0-7 are used for indicating driver type */
@@ -183,40 +58,10 @@ enum {
AZX_SNOOP_TYPE_NVIDIA,
};
-/* HD Audio class code */
-#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
-
struct azx_dev {
- struct snd_dma_buffer bdl; /* BDL buffer */
- u32 *posbuf; /* position buffer pointer */
-
- unsigned int bufsize; /* size of the play buffer in bytes */
- unsigned int period_bytes; /* size of the period in bytes */
- unsigned int frags; /* number for period in the play buffer */
- unsigned int fifo_size; /* FIFO size */
- unsigned long start_wallclk; /* start + minimum wallclk */
- unsigned long period_wallclk; /* wallclk for period */
-
- void __iomem *sd_addr; /* stream descriptor pointer */
-
- u32 sd_int_sta_mask; /* stream int status mask */
-
- /* pcm support */
- struct snd_pcm_substream *substream; /* assigned substream,
- * set in PCM open
- */
- unsigned int format_val; /* format value to be set in the
- * controller and the codec
- */
- unsigned char stream_tag; /* assigned stream */
- unsigned char index; /* stream index */
- int assigned_key; /* last device# key assigned to */
-
- unsigned int opened:1;
- unsigned int running:1;
+ struct hdac_stream core;
+
unsigned int irq_pending:1;
- unsigned int prepared:1;
- unsigned int locked:1;
/*
* For VIA:
* A flag to ensure DMA position is 0
@@ -224,50 +69,17 @@ struct azx_dev {
*/
unsigned int insufficient:1;
unsigned int wc_marked:1;
- unsigned int no_period_wakeup:1;
-
- struct timecounter azx_tc;
- struct cyclecounter azx_cc;
-
- int delay_negative_threshold;
-
-#ifdef CONFIG_SND_HDA_DSP_LOADER
- /* Allows dsp load to have sole access to the playback stream. */
- struct mutex dsp_mutex;
-#endif
};
-/* CORB/RIRB */
-struct azx_rb {
- u32 *buf; /* CORB/RIRB buffer
- * Each CORB entry is 4byte, RIRB is 8byte
- */
- dma_addr_t addr; /* physical address of CORB/RIRB buffer */
- /* for RIRB */
- unsigned short rp, wp; /* read/write pointers */
- int cmds[AZX_MAX_CODECS]; /* number of pending requests */
- u32 res[AZX_MAX_CODECS]; /* last read value */
-};
+#define azx_stream(dev) (&(dev)->core)
+#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core)
struct azx;
/* Functions to read/write to hda registers. */
struct hda_controller_ops {
- /* Register Access */
- void (*reg_writel)(u32 value, u32 __iomem *addr);
- u32 (*reg_readl)(u32 __iomem *addr);
- void (*reg_writew)(u16 value, u16 __iomem *addr);
- u16 (*reg_readw)(u16 __iomem *addr);
- void (*reg_writeb)(u8 value, u8 __iomem *addr);
- u8 (*reg_readb)(u8 __iomem *addr);
/* Disable msi if supported, PCI only */
int (*disable_msi_reset_irq)(struct azx *);
- /* Allocation ops */
- int (*dma_alloc_pages)(struct azx *chip,
- int type,
- size_t size,
- struct snd_dma_buffer *buf);
- void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf);
int (*substream_alloc_pages)(struct azx *chip,
struct snd_pcm_substream *substream,
size_t size);
@@ -277,6 +89,8 @@ struct hda_controller_ops {
struct vm_area_struct *area);
/* Check if current position is acceptable */
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
+ /* enable/disable the link power */
+ int (*link_power)(struct azx *chip, bool enable);
};
struct azx_pcm {
@@ -291,6 +105,8 @@ typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
struct azx {
+ struct hda_bus bus;
+
struct snd_card *card;
struct pci_dev *pci;
int dev_index;
@@ -312,35 +128,16 @@ struct azx {
azx_get_pos_callback_t get_position[2];
azx_get_delay_callback_t get_delay[2];
- /* pci resources */
- unsigned long addr;
- void __iomem *remap_addr;
- int irq;
-
/* locks */
- spinlock_t reg_lock;
struct mutex open_mutex; /* Prevents concurrent open/close operations */
- /* streams (x num_streams) */
- struct azx_dev *azx_dev;
-
/* PCM */
struct list_head pcm_list; /* azx_pcm list */
/* HD codec */
- unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */
- struct hda_bus *bus;
unsigned int beep_mode;
- /* CORB/RIRB */
- struct azx_rb corb;
- struct azx_rb rirb;
-
- /* CORB/RIRB and position buffers */
- struct snd_dma_buffer rb;
- struct snd_dma_buffer posbuf;
-
#ifdef CONFIG_SND_HDA_PATCH_LOADER
const struct firmware *fw;
#endif
@@ -349,7 +146,6 @@ struct azx {
const int *bdl_pos_adj;
int poll_count;
unsigned int running:1;
- unsigned int initialized:1;
unsigned int single_cmd:1;
unsigned int polling_mode:1;
unsigned int msi:1;
@@ -359,14 +155,14 @@ struct azx {
unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by VGA-switcher */
- /* for debugging */
- unsigned int last_cmd[AZX_MAX_CODECS];
-
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev;
#endif
};
+#define azx_bus(chip) (&(chip)->bus.core)
+#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
+
#ifdef CONFIG_X86
#define azx_snoop(chip) ((chip)->snoop)
#else
@@ -378,30 +174,17 @@ struct azx {
*/
#define azx_writel(chip, reg, value) \
- ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg))
+ snd_hdac_chip_writel(azx_bus(chip), reg, value)
#define azx_readl(chip, reg) \
- ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg))
+ snd_hdac_chip_readl(azx_bus(chip), reg)
#define azx_writew(chip, reg, value) \
- ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg))
+ snd_hdac_chip_writew(azx_bus(chip), reg, value)
#define azx_readw(chip, reg) \
- ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg))
+ snd_hdac_chip_readw(azx_bus(chip), reg)
#define azx_writeb(chip, reg, value) \
- ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg))
+ snd_hdac_chip_writeb(azx_bus(chip), reg, value)
#define azx_readb(chip, reg) \
- ((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
-
-#define azx_sd_writel(chip, dev, reg, value) \
- ((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg))
-#define azx_sd_readl(chip, dev, reg) \
- ((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg))
-#define azx_sd_writew(chip, dev, reg, value) \
- ((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg))
-#define azx_sd_readw(chip, dev, reg) \
- ((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg))
-#define azx_sd_writeb(chip, dev, reg, value) \
- ((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg))
-#define azx_sd_readb(chip, dev, reg) \
- ((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg))
+ snd_hdac_chip_readb(azx_bus(chip), reg)
#define azx_has_pm_runtime(chip) \
((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)
@@ -416,22 +199,27 @@ unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
/* Stream control. */
-void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev);
+void azx_stop_all_streams(struct azx *chip);
/* Allocation functions. */
-int azx_alloc_stream_pages(struct azx *chip);
-void azx_free_stream_pages(struct azx *chip);
+#define azx_alloc_stream_pages(chip) \
+ snd_hdac_bus_alloc_stream_pages(azx_bus(chip))
+#define azx_free_stream_pages(chip) \
+ snd_hdac_bus_free_stream_pages(azx_bus(chip))
/* Low level azx interface */
void azx_init_chip(struct azx *chip, bool full_reset);
void azx_stop_chip(struct azx *chip);
-void azx_enter_link_reset(struct azx *chip);
+#define azx_enter_link_reset(chip) \
+ snd_hdac_bus_enter_link_reset(azx_bus(chip))
irqreturn_t azx_interrupt(int irq, void *dev_id);
/* Codec interface */
-int azx_bus_create(struct azx *chip, const char *model);
+int azx_bus_init(struct azx *chip, const char *model,
+ const struct hdac_io_ops *io_ops);
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
int azx_codec_configure(struct azx *chip);
-int azx_init_stream(struct azx *chip);
+int azx_init_streams(struct azx *chip);
+void azx_free_streams(struct azx *chip);
#endif /* __SOUND_HDA_CONTROLLER_H */
diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/pci/hda/hda_controller_trace.h
new file mode 100644
index 000000000000..3e18d99bfb70
--- /dev/null
+++ b/sound/pci/hda/hda_controller_trace.h
@@ -0,0 +1,98 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_controller
+#define TRACE_INCLUDE_FILE hda_controller_trace
+
+#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_CONTROLLER_H
+
+#include <linux/tracepoint.h>
+
+struct azx;
+struct azx_dev;
+
+TRACE_EVENT(azx_pcm_trigger,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
+
+ TP_ARGS(chip, dev, cmd),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( int, cmd )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->core.index;
+ __entry->cmd = cmd;
+ ),
+
+ TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+);
+
+TRACE_EVENT(azx_get_position,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
+
+ TP_ARGS(chip, dev, pos, delay),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( unsigned int, pos )
+ __field( unsigned int, delay )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->core.index;
+ __entry->pos = pos;
+ __entry->delay = delay;
+ ),
+
+ TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+);
+
+DECLARE_EVENT_CLASS(azx_pcm,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+
+ TP_ARGS(chip, azx_dev),
+
+ TP_STRUCT__entry(
+ __field( unsigned char, stream_tag )
+ ),
+
+ TP_fast_assign(
+ __entry->stream_tag = (azx_dev)->core.stream_tag;
+ ),
+
+ TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_open,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_close,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_hw_params,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_prepare,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+#endif /* _TRACE_HDA_CONTROLLER_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 0e6d7534f491..c746cd9a4450 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -448,7 +448,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
hdmi_show_short_audio_desc(codec, e->sad + i);
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void hdmi_print_sad_info(int i, struct cea_sad *a,
struct snd_info_buffer *buffer)
@@ -586,7 +586,7 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
}
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/* update PCM info based on ELD */
void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
deleted file mode 100644
index 3052a2b095f7..000000000000
--- a/sound/pci/hda/hda_i915.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * hda_i915.c - routines for Haswell HDA controller power well support
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/component.h>
-#include <drm/i915_component.h>
-#include <sound/core.h>
-#include "hda_controller.h"
-#include "hda_intel.h"
-
-/* Intel HSW/BDW display HDA controller Extended Mode registers.
- * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display
- * Clock) to 24MHz BCLK: BCLK = CDCLK * M / N
- * The values will be lost when the display power well is disabled.
- */
-#define AZX_REG_EM4 0x100c
-#define AZX_REG_EM5 0x1010
-
-int hda_display_power(struct hda_intel *hda, bool enable)
-{
- struct i915_audio_component *acomp = &hda->audio_component;
-
- if (!acomp->ops)
- return -ENODEV;
-
- dev_dbg(&hda->chip.pci->dev, "display power %s\n",
- enable ? "enable" : "disable");
- if (enable)
- acomp->ops->get_power(acomp->dev);
- else
- acomp->ops->put_power(acomp->dev);
-
- return 0;
-}
-
-void haswell_set_bclk(struct hda_intel *hda)
-{
- int cdclk_freq;
- unsigned int bclk_m, bclk_n;
- struct i915_audio_component *acomp = &hda->audio_component;
- struct pci_dev *pci = hda->chip.pci;
-
- /* Only Haswell/Broadwell need set BCLK */
- if (pci->device != 0x0a0c && pci->device != 0x0c0c
- && pci->device != 0x0d0c && pci->device != 0x160c)
- return;
-
- if (!acomp->ops)
- return;
-
- cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
- switch (cdclk_freq) {
- case 337500:
- bclk_m = 16;
- bclk_n = 225;
- break;
-
- case 450000:
- default: /* default CDCLK 450MHz */
- bclk_m = 4;
- bclk_n = 75;
- break;
-
- case 540000:
- bclk_m = 4;
- bclk_n = 90;
- break;
-
- case 675000:
- bclk_m = 8;
- bclk_n = 225;
- break;
- }
-
- azx_writew(&hda->chip, EM4, bclk_m);
- azx_writew(&hda->chip, EM5, bclk_n);
-}
-
-static int hda_component_master_bind(struct device *dev)
-{
- struct snd_card *card = dev_get_drvdata(dev);
- struct azx *chip = card->private_data;
- struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
- struct i915_audio_component *acomp = &hda->audio_component;
- int ret;
-
- ret = component_bind_all(dev, acomp);
- if (ret < 0)
- return ret;
-
- if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
- acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
- ret = -EINVAL;
- goto out_unbind;
- }
-
- /*
- * Atm, we don't support dynamic unbinding initiated by the child
- * component, so pin its containing module until we unbind.
- */
- if (!try_module_get(acomp->ops->owner)) {
- ret = -ENODEV;
- goto out_unbind;
- }
-
- return 0;
-
-out_unbind:
- component_unbind_all(dev, acomp);
-
- return ret;
-}
-
-static void hda_component_master_unbind(struct device *dev)
-{
- struct snd_card *card = dev_get_drvdata(dev);
- struct azx *chip = card->private_data;
- struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
- struct i915_audio_component *acomp = &hda->audio_component;
-
- module_put(acomp->ops->owner);
- component_unbind_all(dev, acomp);
- WARN_ON(acomp->ops || acomp->dev);
-}
-
-static const struct component_master_ops hda_component_master_ops = {
- .bind = hda_component_master_bind,
- .unbind = hda_component_master_unbind,
-};
-
-static int hda_component_master_match(struct device *dev, void *data)
-{
- /* i915 is the only supported component */
- return !strcmp(dev->driver->name, "i915");
-}
-
-int hda_i915_init(struct hda_intel *hda)
-{
- struct component_match *match = NULL;
- struct device *dev = &hda->chip.pci->dev;
- struct i915_audio_component *acomp = &hda->audio_component;
- int ret;
-
- component_match_add(dev, &match, hda_component_master_match, hda);
- ret = component_master_add_with_match(dev, &hda_component_master_ops,
- match);
- if (ret < 0)
- goto out_err;
-
- /*
- * Atm, we don't support deferring the component binding, so make sure
- * i915 is loaded and that the binding successfully completes.
- */
- request_module("i915");
-
- if (!acomp->ops) {
- ret = -ENODEV;
- goto out_master_del;
- }
-
- dev_dbg(dev, "bound to i915 component master\n");
-
- return 0;
-out_master_del:
- component_master_del(dev, &hda_component_master_ops);
-out_err:
- dev_err(dev, "failed to add i915 component master (%d)\n", ret);
-
- return ret;
-}
-
-int hda_i915_exit(struct hda_intel *hda)
-{
- struct device *dev = &hda->chip.pci->dev;
-
- component_master_del(dev, &hda_component_master_ops);
-
- return 0;
-}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b6db25b23dd3..7dea7987d2af 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -57,6 +57,8 @@
#endif
#include <sound/core.h>
#include <sound/initval.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
#include <linux/firmware.h>
@@ -64,6 +66,9 @@
#include "hda_controller.h"
#include "hda_intel.h"
+#define CREATE_TRACE_POINTS
+#include "hda_intel_trace.h"
+
/* position fix mode */
enum {
POS_FIX_AUTO,
@@ -496,11 +501,22 @@ static void azx_init_pci(struct azx *chip)
}
}
+static void hda_intel_init_chip(struct azx *chip, bool full_reset)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ snd_hdac_set_codec_wakeup(bus, true);
+ azx_init_chip(chip, full_reset);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ snd_hdac_set_codec_wakeup(bus, false);
+}
+
/* calculate runtime delay from LPIB */
static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev,
unsigned int pos)
{
- struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
int stream = substream->stream;
unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev);
int delay;
@@ -510,16 +526,16 @@ static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev,
else
delay = lpib_pos - pos;
if (delay < 0) {
- if (delay >= azx_dev->delay_negative_threshold)
+ if (delay >= azx_dev->core.delay_negative_threshold)
delay = 0;
else
- delay += azx_dev->bufsize;
+ delay += azx_dev->core.bufsize;
}
- if (delay >= azx_dev->period_bytes) {
+ if (delay >= azx_dev->core.period_bytes) {
dev_info(chip->card->dev,
"Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
- delay, azx_dev->period_bytes);
+ delay, azx_dev->core.period_bytes);
delay = 0;
chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
chip->get_delay[stream] = NULL;
@@ -548,6 +564,14 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
return 0;
}
+/* Enable/disable i915 display power for the link */
+static int azx_intel_link_power(struct azx *chip, bool enable)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+
+ return snd_hdac_display_power(bus, enable);
+}
+
/*
* Check whether the current DMA position is acceptable for updating
* periods. Returns non-zero if it's OK.
@@ -559,13 +583,13 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
*/
static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
- struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
int stream = substream->stream;
u32 wallclk;
unsigned int pos;
- wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
- if (wallclk < (azx_dev->period_wallclk * 2) / 3)
+ wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
+ if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
return -1; /* bogus (too early) interrupt */
if (chip->get_position[stream])
@@ -576,6 +600,9 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
dev_info(chip->card->dev,
"Invalid position buffer, using LPIB read method instead.\n");
chip->get_position[stream] = azx_get_pos_lpib;
+ if (chip->get_position[0] == azx_get_pos_lpib &&
+ chip->get_position[1] == azx_get_pos_lpib)
+ azx_bus(chip)->use_posbuf = false;
pos = azx_get_pos_lpib(chip, azx_dev);
chip->get_delay[stream] = NULL;
} else {
@@ -585,17 +612,17 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
}
}
- if (pos >= azx_dev->bufsize)
+ if (pos >= azx_dev->core.bufsize)
pos = 0;
- if (WARN_ONCE(!azx_dev->period_bytes,
+ if (WARN_ONCE(!azx_dev->core.period_bytes,
"hda-intel: zero azx_dev->period_bytes"))
return -1; /* this shouldn't happen! */
- if (wallclk < (azx_dev->period_wallclk * 5) / 4 &&
- pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
+ if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 &&
+ pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2)
/* NG - it's below the first next period boundary */
return chip->bdl_pos_adj[chip->dev_index] ? 0 : -1;
- azx_dev->start_wallclk += wallclk;
+ azx_dev->core.start_wallclk += wallclk;
return 1; /* OK, it's fine */
}
@@ -606,7 +633,9 @@ static void azx_irq_pending_work(struct work_struct *work)
{
struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work);
struct azx *chip = &hda->chip;
- int i, pending, ok;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+ int pending, ok;
if (!hda->irq_pending_warned) {
dev_info(chip->card->dev,
@@ -617,25 +646,25 @@ static void azx_irq_pending_work(struct work_struct *work)
for (;;) {
pending = 0;
- spin_lock_irq(&chip->reg_lock);
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
if (!azx_dev->irq_pending ||
- !azx_dev->substream ||
- !azx_dev->running)
+ !s->substream ||
+ !s->running)
continue;
ok = azx_position_ok(chip, azx_dev);
if (ok > 0) {
azx_dev->irq_pending = 0;
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
+ spin_unlock(&bus->reg_lock);
+ snd_pcm_period_elapsed(s->substream);
+ spin_lock(&bus->reg_lock);
} else if (ok < 0) {
pending = 0; /* too early */
} else
pending++;
}
- spin_unlock_irq(&chip->reg_lock);
+ spin_unlock_irq(&bus->reg_lock);
if (!pending)
return;
msleep(1);
@@ -645,16 +674,21 @@ static void azx_irq_pending_work(struct work_struct *work)
/* clear irq_pending flags and assure no on-going workq */
static void azx_clear_irq_pending(struct azx *chip)
{
- int i;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
- spin_lock_irq(&chip->reg_lock);
- for (i = 0; i < chip->num_streams; i++)
- chip->azx_dev[i].irq_pending = 0;
- spin_unlock_irq(&chip->reg_lock);
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+ azx_dev->irq_pending = 0;
+ }
+ spin_unlock_irq(&bus->reg_lock);
}
static int azx_acquire_irq(struct azx *chip, int do_disconnect)
{
+ struct hdac_bus *bus = azx_bus(chip);
+
if (request_irq(chip->pci->irq, azx_interrupt,
chip->msi ? 0 : IRQF_SHARED,
KBUILD_MODNAME, chip)) {
@@ -665,7 +699,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
snd_card_disconnect(chip->card);
return -1;
}
- chip->irq = chip->pci->irq;
+ bus->irq = chip->pci->irq;
pci_intx(chip->pci, !chip->msi);
return 0;
}
@@ -678,8 +712,8 @@ static unsigned int azx_via_get_position(struct azx *chip,
unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
unsigned int fifo_size;
- link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB);
- if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+ if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* Playback, no problem using link position */
return link_pos;
}
@@ -688,13 +722,14 @@ static unsigned int azx_via_get_position(struct azx *chip,
/* For new chipset,
* use mod to get the DMA position just like old chipset
*/
- mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
- mod_dma_pos %= azx_dev->period_bytes;
+ mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
+ mod_dma_pos %= azx_dev->core.period_bytes;
/* azx_dev->fifo_size can't get FIFO size of in stream.
* Get from base address + offset.
*/
- fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
+ fifo_size = readw(azx_bus(chip)->remap_addr +
+ VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
if (azx_dev->insufficient) {
/* Link position never gather than FIFO size */
@@ -705,20 +740,20 @@ static unsigned int azx_via_get_position(struct azx *chip,
}
if (link_pos <= fifo_size)
- mini_pos = azx_dev->bufsize + link_pos - fifo_size;
+ mini_pos = azx_dev->core.bufsize + link_pos - fifo_size;
else
mini_pos = link_pos - fifo_size;
/* Find nearest previous boudary */
- mod_mini_pos = mini_pos % azx_dev->period_bytes;
- mod_link_pos = link_pos % azx_dev->period_bytes;
+ mod_mini_pos = mini_pos % azx_dev->core.period_bytes;
+ mod_link_pos = link_pos % azx_dev->core.period_bytes;
if (mod_link_pos >= fifo_size)
bound_pos = link_pos - mod_link_pos;
else if (mod_dma_pos >= mod_mini_pos)
bound_pos = mini_pos - mod_mini_pos;
else {
- bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
- if (bound_pos >= azx_dev->bufsize)
+ bound_pos = mini_pos - mod_mini_pos + azx_dev->core.period_bytes;
+ if (bound_pos >= azx_dev->core.bufsize)
bound_pos = 0;
}
@@ -760,9 +795,9 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
mutex_lock(&card_list_lock);
list_for_each_entry(hda, &card_list, list) {
chip = &hda->chip;
- if (!chip->bus || chip->disabled)
+ if (!hda->probe_continued || chip->disabled)
continue;
- snd_hda_set_power_save(chip->bus, power_save * 1000);
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
}
mutex_unlock(&card_list_lock);
return 0;
@@ -772,6 +807,50 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
#define azx_del_card_list(chip) /* NOP */
#endif /* CONFIG_PM */
+/* Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
+ * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value)
+ * are used to convert CDClk (Core Display Clock) to 24MHz BCLK:
+ * BCLK = CDCLK * M / N
+ * The values will be lost when the display power well is disabled and need to
+ * be restored to avoid abnormal playback speed.
+ */
+static void haswell_set_bclk(struct hda_intel *hda)
+{
+ struct azx *chip = &hda->chip;
+ int cdclk_freq;
+ unsigned int bclk_m, bclk_n;
+
+ if (!hda->need_i915_power)
+ return;
+
+ cdclk_freq = snd_hdac_get_display_clk(azx_bus(chip));
+ switch (cdclk_freq) {
+ case 337500:
+ bclk_m = 16;
+ bclk_n = 225;
+ break;
+
+ case 450000:
+ default: /* default CDCLK 450MHz */
+ bclk_m = 4;
+ bclk_n = 75;
+ break;
+
+ case 540000:
+ bclk_m = 4;
+ bclk_n = 90;
+ break;
+
+ case 675000:
+ bclk_m = 8;
+ bclk_n = 225;
+ break;
+ }
+
+ azx_writew(chip, HSW_EM4, bclk_m);
+ azx_writew(chip, HSW_EM5, bclk_n);
+}
+
#if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
/*
* power management
@@ -781,6 +860,7 @@ static int azx_suspend(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hda_intel *hda;
+ struct hdac_bus *bus;
if (!card)
return 0;
@@ -790,19 +870,23 @@ static int azx_suspend(struct device *dev)
if (chip->disabled || hda->init_failed)
return 0;
+ bus = azx_bus(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
azx_clear_irq_pending(chip);
azx_stop_chip(chip);
azx_enter_link_reset(chip);
- if (chip->irq >= 0) {
- free_irq(chip->irq, chip);
- chip->irq = -1;
+ if (bus->irq >= 0) {
+ free_irq(bus->irq, chip);
+ bus->irq = -1;
}
if (chip->msi)
pci_disable_msi(chip->pci);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- hda_display_power(hda, false);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power)
+ snd_hdac_display_power(bus, false);
+
+ trace_azx_suspend(chip);
return 0;
}
@@ -821,8 +905,9 @@ static int azx_resume(struct device *dev)
if (chip->disabled || hda->init_failed)
return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- hda_display_power(hda, true);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power) {
+ snd_hdac_display_power(azx_bus(chip), true);
haswell_set_bclk(hda);
}
if (chip->msi)
@@ -832,9 +917,11 @@ static int azx_resume(struct device *dev)
return -EIO;
azx_init_pci(chip);
- azx_init_chip(chip, true);
+ hda_intel_init_chip(chip, true);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ trace_azx_resume(chip);
return 0;
}
#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
@@ -864,9 +951,11 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip);
azx_enter_link_reset(chip);
azx_clear_irq_pending(chip);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
- hda_display_power(hda, false);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power)
+ snd_hdac_display_power(azx_bus(chip), false);
+ trace_azx_runtime_suspend(chip);
return 0;
}
@@ -875,7 +964,7 @@ static int azx_runtime_resume(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hda_intel *hda;
- struct hda_bus *bus;
+ struct hdac_bus *bus;
struct hda_codec *codec;
int status;
@@ -890,20 +979,24 @@ static int azx_runtime_resume(struct device *dev)
if (!azx_has_pm_runtime(chip))
return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- hda_display_power(hda, true);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power) {
+ bus = azx_bus(chip);
+ snd_hdac_display_power(bus, true);
haswell_set_bclk(hda);
+ /* toggle codec wakeup bit for STATESTS read */
+ snd_hdac_set_codec_wakeup(bus, true);
+ snd_hdac_set_codec_wakeup(bus, false);
}
/* Read STATESTS before controller reset */
status = azx_readw(chip, STATESTS);
azx_init_pci(chip);
- azx_init_chip(chip, true);
+ hda_intel_init_chip(chip, true);
- bus = chip->bus;
- if (status && bus) {
- list_for_each_codec(codec, bus)
+ if (status) {
+ list_for_each_codec(codec, &chip->bus)
if (status & (1 << codec->addr))
schedule_delayed_work(&codec->jackpoll_work,
codec->jackpoll_interval);
@@ -913,6 +1006,7 @@ static int azx_runtime_resume(struct device *dev)
azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
~STATESTS_INT_MASK);
+ trace_azx_runtime_resume(chip);
return 0;
}
@@ -931,7 +1025,7 @@ static int azx_runtime_idle(struct device *dev)
return 0;
if (!power_save_controller || !azx_has_pm_runtime(chip) ||
- chip->bus->core.codec_powered)
+ azx_bus(chip)->codec_powered)
return -EBUSY;
return 0;
@@ -969,7 +1063,7 @@ static void azx_vs_set_state(struct pci_dev *pci,
if (chip->disabled == disabled)
return;
- if (!chip->bus) {
+ if (!hda->probe_continued) {
chip->disabled = disabled;
if (!disabled) {
dev_info(chip->card->dev,
@@ -990,11 +1084,11 @@ static void azx_vs_set_state(struct pci_dev *pci,
* put ourselves there */
pci->current_state = PCI_D3cold;
chip->disabled = true;
- if (snd_hda_lock_devices(chip->bus))
+ if (snd_hda_lock_devices(&chip->bus))
dev_warn(chip->card->dev,
"Cannot lock devices!\n");
} else {
- snd_hda_unlock_devices(chip->bus);
+ snd_hda_unlock_devices(&chip->bus);
pm_runtime_get_noresume(card->dev);
chip->disabled = false;
azx_resume(card->dev);
@@ -1011,11 +1105,11 @@ static bool azx_vs_can_switch(struct pci_dev *pci)
wait_for_completion(&hda->probe_wait);
if (hda->init_failed)
return false;
- if (chip->disabled || !chip->bus)
+ if (chip->disabled || !hda->probe_continued)
return true;
- if (snd_hda_lock_devices(chip->bus))
+ if (snd_hda_lock_devices(&chip->bus))
return false;
- snd_hda_unlock_devices(chip->bus);
+ snd_hda_unlock_devices(&chip->bus);
return true;
}
@@ -1048,7 +1142,7 @@ static int register_vga_switcheroo(struct azx *chip)
*/
err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops,
VGA_SWITCHEROO_DIS,
- chip->bus != NULL);
+ hda->probe_continued);
if (err < 0)
return err;
hda->vga_switcheroo_registered = 1;
@@ -1071,7 +1165,7 @@ static int azx_free(struct azx *chip)
{
struct pci_dev *pci = chip->pci;
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
- int i;
+ struct hdac_bus *bus = azx_bus(chip);
if (azx_has_pm_runtime(chip) && chip->running)
pm_runtime_get_noresume(&pci->dev);
@@ -1082,42 +1176,54 @@ static int azx_free(struct azx *chip)
complete_all(&hda->probe_wait);
if (use_vga_switcheroo(hda)) {
- if (chip->disabled && chip->bus)
- snd_hda_unlock_devices(chip->bus);
+ if (chip->disabled && hda->probe_continued)
+ snd_hda_unlock_devices(&chip->bus);
if (hda->vga_switcheroo_registered)
vga_switcheroo_unregister_client(chip->pci);
}
- if (chip->initialized) {
+ if (bus->chip_init) {
azx_clear_irq_pending(chip);
- for (i = 0; i < chip->num_streams; i++)
- azx_stream_stop(chip, &chip->azx_dev[i]);
+ azx_stop_all_streams(chip);
azx_stop_chip(chip);
}
- if (chip->irq >= 0)
- free_irq(chip->irq, (void*)chip);
+ if (bus->irq >= 0)
+ free_irq(bus->irq, (void*)chip);
if (chip->msi)
pci_disable_msi(chip->pci);
- iounmap(chip->remap_addr);
+ iounmap(bus->remap_addr);
azx_free_stream_pages(chip);
+ azx_free_streams(chip);
+ snd_hdac_bus_exit(bus);
+
if (chip->region_requested)
pci_release_regions(chip->pci);
+
pci_disable_device(chip->pci);
- kfree(chip->azx_dev);
#ifdef CONFIG_SND_HDA_PATCH_LOADER
release_firmware(chip->fw);
#endif
+
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- hda_display_power(hda, false);
- hda_i915_exit(hda);
+ if (hda->need_i915_power)
+ snd_hdac_display_power(bus, false);
+ snd_hdac_i915_exit(bus);
}
kfree(hda);
return 0;
}
+static int azx_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+
+ chip->bus.shutdown = 1;
+ return 0;
+}
+
static int azx_dev_free(struct snd_device *device)
{
return azx_free(device->device_data);
@@ -1284,9 +1390,9 @@ static void check_probe_mask(struct azx *chip, int dev)
/* check forced option */
if (chip->codec_probe_mask != -1 &&
(chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
- chip->codec_mask = chip->codec_probe_mask & 0xff;
+ azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff;
dev_info(chip->card->dev, "codec_mask forced to 0x%x\n",
- chip->codec_mask);
+ (int)azx_bus(chip)->codec_mask);
}
}
@@ -1373,12 +1479,15 @@ static void azx_probe_work(struct work_struct *work)
/*
* constructor
*/
+static const struct hdac_io_ops pci_hda_io_ops;
+static const struct hda_controller_ops pci_hda_ops;
+
static int azx_create(struct snd_card *card, struct pci_dev *pci,
int dev, unsigned int driver_caps,
- const struct hda_controller_ops *hda_ops,
struct azx **rchip)
{
static struct snd_device_ops ops = {
+ .dev_disconnect = azx_dev_disconnect,
.dev_free = azx_dev_free,
};
struct hda_intel *hda;
@@ -1398,12 +1507,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
}
chip = &hda->chip;
- spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
- chip->ops = hda_ops;
- chip->irq = -1;
+ chip->ops = &pci_hda_ops;
chip->driver_caps = driver_caps;
chip->driver_type = driver_caps & 0xff;
check_msi(chip);
@@ -1435,6 +1542,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
}
chip->bdl_pos_adj = bdl_pos_adj;
+ err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
+ if (err < 0) {
+ kfree(hda);
+ pci_disable_device(pci);
+ return err;
+ }
+
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
dev_err(card->dev, "Error creating device [card]!\n");
@@ -1455,6 +1569,7 @@ static int azx_first_init(struct azx *chip)
int dev = chip->dev_index;
struct pci_dev *pci = chip->pci;
struct snd_card *card = chip->card;
+ struct hdac_bus *bus = azx_bus(chip);
int err;
unsigned short gcap;
unsigned int dma_bits = 64;
@@ -1474,9 +1589,9 @@ static int azx_first_init(struct azx *chip)
return err;
chip->region_requested = 1;
- chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
+ bus->addr = pci_resource_start(pci, 0);
+ bus->remap_addr = pci_ioremap_bar(pci, 0);
+ if (bus->remap_addr == NULL) {
dev_err(card->dev, "ioremap error\n");
return -ENXIO;
}
@@ -1494,7 +1609,7 @@ static int azx_first_init(struct azx *chip)
return -EBUSY;
pci_set_master(pci);
- synchronize_irq(chip->irq);
+ synchronize_irq(bus->irq);
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
@@ -1536,11 +1651,11 @@ static int azx_first_init(struct azx *chip)
/* allow 64bit DMA address if supported by H/W */
if (!(gcap & AZX_GCAP_64OK))
dma_bits = 32;
- if (!pci_set_dma_mask(pci, DMA_BIT_MASK(dma_bits))) {
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(dma_bits));
+ if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
} else {
- pci_set_dma_mask(pci, DMA_BIT_MASK(32));
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
+ dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32));
}
/* read number of streams from GCAP register instead of using
@@ -1571,17 +1686,15 @@ static int azx_first_init(struct azx *chip)
chip->capture_index_offset = 0;
chip->playback_index_offset = chip->capture_streams;
chip->num_streams = chip->playback_streams + chip->capture_streams;
- chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
- GFP_KERNEL);
- if (!chip->azx_dev)
- return -ENOMEM;
- err = azx_alloc_stream_pages(chip);
+ /* initialize streams */
+ err = azx_init_streams(chip);
if (err < 0)
return err;
- /* initialize streams */
- azx_init_stream(chip);
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0)
+ return err;
/* initialize chip */
azx_init_pci(chip);
@@ -1593,10 +1706,10 @@ static int azx_first_init(struct azx *chip)
haswell_set_bclk(hda);
}
- azx_init_chip(chip, (probe_only[dev] & 2) == 0);
+ hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0);
/* codec detection */
- if (!chip->codec_mask) {
+ if (!azx_bus(chip)->codec_mask) {
dev_err(card->dev, "no codecs found!\n");
return -ENODEV;
}
@@ -1606,7 +1719,7 @@ static int azx_first_init(struct azx *chip)
sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
- card->shortname, chip->addr, chip->irq);
+ card->shortname, bus->addr, bus->irq);
return 0;
}
@@ -1675,10 +1788,11 @@ static u8 pci_azx_readb(u8 __iomem *addr)
static int disable_msi_reset_irq(struct azx *chip)
{
+ struct hdac_bus *bus = azx_bus(chip);
int err;
- free_irq(chip->irq, chip);
- chip->irq = -1;
+ free_irq(bus->irq, chip);
+ bus->irq = -1;
pci_disable_msi(chip->pci);
chip->msi = 0;
err = azx_acquire_irq(chip, 1);
@@ -1689,15 +1803,16 @@ static int disable_msi_reset_irq(struct azx *chip)
}
/* DMA page allocation helpers. */
-static int dma_alloc_pages(struct azx *chip,
+static int dma_alloc_pages(struct hdac_bus *bus,
int type,
size_t size,
struct snd_dma_buffer *buf)
{
+ struct azx *chip = bus_to_azx(bus);
int err;
err = snd_dma_alloc_pages(type,
- chip->card->dev,
+ bus->dev,
size, buf);
if (err < 0)
return err;
@@ -1705,8 +1820,10 @@ static int dma_alloc_pages(struct azx *chip,
return 0;
}
-static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
+static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
{
+ struct azx *chip = bus_to_azx(bus);
+
mark_pages_wc(chip, buf, false);
snd_dma_free_pages(buf);
}
@@ -1719,9 +1836,6 @@ static int substream_alloc_pages(struct azx *chip,
int ret;
mark_runtime_wc(chip, azx_dev, substream, false);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
ret = snd_pcm_lib_malloc_pages(substream, size);
if (ret < 0)
return ret;
@@ -1748,20 +1862,24 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
#endif
}
-static const struct hda_controller_ops pci_hda_ops = {
+static const struct hdac_io_ops pci_hda_io_ops = {
.reg_writel = pci_azx_writel,
.reg_readl = pci_azx_readl,
.reg_writew = pci_azx_writew,
.reg_readw = pci_azx_readw,
.reg_writeb = pci_azx_writeb,
.reg_readb = pci_azx_readb,
- .disable_msi_reset_irq = disable_msi_reset_irq,
.dma_alloc_pages = dma_alloc_pages,
.dma_free_pages = dma_free_pages,
+};
+
+static const struct hda_controller_ops pci_hda_ops = {
+ .disable_msi_reset_irq = disable_msi_reset_irq,
.substream_alloc_pages = substream_alloc_pages,
.substream_free_pages = substream_free_pages,
.pcm_mmap_prepare = pcm_mmap_prepare,
.position_check = azx_position_check,
+ .link_power = azx_intel_link_power,
};
static int azx_probe(struct pci_dev *pci,
@@ -1788,8 +1906,7 @@ static int azx_probe(struct pci_dev *pci,
return err;
}
- err = azx_create(card, pci, dev, pci_id->driver_data,
- &pci_hda_ops, &chip);
+ err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0)
goto out_free;
card->private_data = chip;
@@ -1851,14 +1968,24 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
static int azx_probe_continue(struct azx *chip)
{
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
struct pci_dev *pci = chip->pci;
int dev = chip->dev_index;
int err;
- /* Request power well for Haswell HDA controller and codec */
+ hda->probe_continued = 1;
+
+ /* Request display power well for the HDA controller or codec. For
+ * Haswell/Broadwell, both the display HDA controller and codec need
+ * this power. For other platforms, like Baytrail/Braswell, only the
+ * display codec needs the power and it can be released after probe.
+ */
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
-#ifdef CONFIG_SND_HDA_I915
- err = hda_i915_init(hda);
+ /* HSW/BDW controllers need this power */
+ if (CONTROLLER_IN_GPU(pci))
+ hda->need_i915_power = 1;
+
+ err = snd_hdac_i915_init(bus);
if (err < 0) {
/* if the controller is bound only with HDMI/DP
* (for HSW and BDW), we need to abort the probe;
@@ -1870,18 +1997,16 @@ static int azx_probe_continue(struct azx *chip)
else
goto skip_i915;
}
- err = hda_display_power(hda, true);
+
+ err = snd_hdac_display_power(bus, true);
if (err < 0) {
dev_err(chip->card->dev,
"Cannot turn on display power on i915\n");
- goto out_free;
+ goto i915_power_fail;
}
-#endif
}
-#ifdef CONFIG_SND_HDA_I915
skip_i915:
-#endif
err = azx_first_init(chip);
if (err < 0)
goto out_free;
@@ -1891,17 +2016,13 @@ static int azx_probe_continue(struct azx *chip)
#endif
/* create codec instances */
- err = azx_bus_create(chip, model[dev]);
- if (err < 0)
- goto out_free;
-
err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
if (err < 0)
goto out_free;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
if (chip->fw) {
- err = snd_hda_load_patch(chip->bus, chip->fw->size,
+ err = snd_hda_load_patch(&chip->bus, chip->fw->size,
chip->fw->data);
if (err < 0)
goto out_free;
@@ -1923,11 +2044,16 @@ static int azx_probe_continue(struct azx *chip)
chip->running = 1;
azx_add_card_list(chip);
- snd_hda_set_power_save(chip->bus, power_save * 1000);
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
pm_runtime_put_noidle(&pci->dev);
out_free:
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && !hda->need_i915_power)
+ snd_hdac_display_power(bus, false);
+
+i915_power_fail:
if (err < 0)
hda->init_failed = 1;
complete_all(&hda->probe_wait);
diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
index d5231f7216a7..354f0bbed833 100644
--- a/sound/pci/hda/hda_intel.h
+++ b/sound/pci/hda/hda_intel.h
@@ -16,7 +16,6 @@
#ifndef __SOUND_HDA_INTEL_H
#define __SOUND_HDA_INTEL_H
-#include <drm/i915_component.h>
#include "hda_controller.h"
struct hda_intel {
@@ -34,6 +33,7 @@ struct hda_intel {
/* extra flags */
unsigned int irq_pending_warned:1;
+ unsigned int probe_continued:1;
/* VGA-switcheroo setup */
unsigned int use_vga_switcheroo:1;
@@ -43,29 +43,7 @@ struct hda_intel {
/* secondary power domain for hdmi audio under vga device */
struct dev_pm_domain hdmi_pm_domain;
- /* i915 component interface */
- struct i915_audio_component audio_component;
+ bool need_i915_power:1; /* the hda controller needs i915 power */
};
-#ifdef CONFIG_SND_HDA_I915
-int hda_display_power(struct hda_intel *hda, bool enable);
-void haswell_set_bclk(struct hda_intel *hda);
-int hda_i915_init(struct hda_intel *hda);
-int hda_i915_exit(struct hda_intel *hda);
-#else
-static inline int hda_display_power(struct hda_intel *hda, bool enable)
-{
- return 0;
-}
-static inline void haswell_set_bclk(struct hda_intel *hda) { return; }
-static inline int hda_i915_init(struct hda_intel *hda)
-{
- return -ENODEV;
-}
-static inline int hda_i915_exit(struct hda_intel *hda)
-{
- return 0;
-}
-#endif
-
#endif
diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h
index 7b5e4c2cf9d5..0922d8b1b17d 100644
--- a/sound/pci/hda/hda_intel_trace.h
+++ b/sound/pci/hda/hda_intel_trace.h
@@ -7,52 +7,43 @@
#include <linux/tracepoint.h>
-struct azx;
-struct azx_dev;
+DECLARE_EVENT_CLASS(hda_pm,
+ TP_PROTO(struct azx *chip),
-TRACE_EVENT(azx_pcm_trigger,
-
- TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
-
- TP_ARGS(chip, dev, cmd),
+ TP_ARGS(chip),
TP_STRUCT__entry(
- __field( int, card )
- __field( int, idx )
- __field( int, cmd )
+ __field(int, dev_index)
),
TP_fast_assign(
- __entry->card = (chip)->card->number;
- __entry->idx = (dev)->index;
- __entry->cmd = cmd;
+ __entry->dev_index = (chip)->dev_index;
),
- TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+ TP_printk("card index: %d", __entry->dev_index)
);
-TRACE_EVENT(azx_get_position,
-
- TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
-
- TP_ARGS(chip, dev, pos, delay),
+DEFINE_EVENT(hda_pm, azx_suspend,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
- TP_STRUCT__entry(
- __field( int, card )
- __field( int, idx )
- __field( unsigned int, pos )
- __field( unsigned int, delay )
- ),
+DEFINE_EVENT(hda_pm, azx_resume,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
- TP_fast_assign(
- __entry->card = (chip)->card->number;
- __entry->idx = (dev)->index;
- __entry->pos = pos;
- __entry->delay = delay;
- ),
+#ifdef CONFIG_PM
+DEFINE_EVENT(hda_pm, azx_runtime_suspend,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
- TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+DEFINE_EVENT(hda_pm, azx_runtime_resume,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
);
+#endif
#endif /* _TRACE_HDA_INTEL_H */
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index d7cfe7b8c32b..366efbf87d41 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -132,11 +132,11 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
for (i = 0; i < codec->jacktbl.used; i++, jack++) {
struct hda_jack_callback *cb, *next;
-#ifdef CONFIG_SND_HDA_INPUT_JACK
+
/* free jack instances manually when clearing/reconfiguring */
if (!codec->bus->shutdown && jack->jack)
snd_device_free(codec->card, jack->jack);
-#endif
+
for (cb = jack->callback; cb; cb = next) {
next = cb->next;
kfree(cb);
@@ -337,20 +337,15 @@ void snd_hda_jack_report_sync(struct hda_codec *codec)
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) {
- if (!jack->kctl || jack->block_report)
+ if (!jack->jack || jack->block_report)
continue;
state = get_jack_plug_state(jack->pin_sense);
- snd_kctl_jack_report(codec->card, jack->kctl, state);
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- if (jack->jack)
- snd_jack_report(jack->jack,
- state ? jack->type : 0);
-#endif
+ snd_jack_report(jack->jack,
+ state ? jack->type : 0);
}
}
EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
-#ifdef CONFIG_SND_HDA_INPUT_JACK
/* guess the jack type from the pin-config */
static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
{
@@ -377,54 +372,42 @@ static void hda_free_jack_priv(struct snd_jack *jack)
jacks->nid = 0;
jacks->jack = NULL;
}
-#endif
/**
* snd_hda_jack_add_kctl - Add a kctl for the given pin
* @codec: the HDA codec
* @nid: pin NID to assign
* @name: string name for the jack
- * @idx: index number for the jack
* @phantom_jack: flag to deal as a phantom jack
*
* This assigns a jack-detection kctl to the given pin. The kcontrol
* will have the given name and index.
*/
static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
- const char *name, int idx, bool phantom_jack)
+ const char *name, bool phantom_jack)
{
struct hda_jack_tbl *jack;
- struct snd_kcontrol *kctl;
- int err, state;
+ int err, state, type;
jack = snd_hda_jack_tbl_new(codec, nid);
if (!jack)
return 0;
- if (jack->kctl)
+ if (jack->jack)
return 0; /* already created */
- kctl = snd_kctl_jack_new(name, idx, codec);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, nid, kctl);
+
+ type = get_input_jack_type(codec, nid);
+ err = snd_jack_new(codec->card, name, type,
+ &jack->jack, true, phantom_jack);
if (err < 0)
return err;
- jack->kctl = kctl;
- jack->phantom_jack = !!phantom_jack;
+ jack->phantom_jack = !!phantom_jack;
+ jack->type = type;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = hda_free_jack_priv;
state = snd_hda_jack_detect(codec, nid);
- snd_kctl_jack_report(codec->card, kctl, state);
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- if (!phantom_jack) {
- jack->type = get_input_jack_type(codec, nid);
- err = snd_jack_new(codec->card, name, jack->type,
- &jack->jack);
- if (err < 0)
- return err;
- jack->jack->private_data = jack;
- jack->jack->private_free = hda_free_jack_priv;
- snd_jack_report(jack->jack, state ? jack->type : 0);
- }
-#endif
+ snd_jack_report(jack->jack, state ? jack->type : 0);
+
return 0;
}
@@ -433,44 +416,23 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
* @codec: the HDA codec
* @nid: pin NID
* @name: the name string for the jack ctl
- * @idx: the ctl index for the jack ctl
*
* This is a simple helper calling __snd_hda_jack_add_kctl().
*/
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
- const char *name, int idx)
+ const char *name)
{
- return __snd_hda_jack_add_kctl(codec, nid, name, idx, false);
+ return __snd_hda_jack_add_kctl(codec, nid, name, false);
}
EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl);
-/* get the unique index number for the given kctl name */
-static int get_unique_index(struct hda_codec *codec, const char *name, int idx)
-{
- struct hda_jack_tbl *jack;
- int i, len = strlen(name);
- again:
- jack = codec->jacktbl.list;
- for (i = 0; i < codec->jacktbl.used; i++, jack++) {
- /* jack->kctl.id contains "XXX Jack" name string with index */
- if (jack->kctl &&
- !strncmp(name, jack->kctl->id.name, len) &&
- !strcmp(" Jack", jack->kctl->id.name + len) &&
- jack->kctl->id.index == idx) {
- idx++;
- goto again;
- }
- }
- return idx;
-}
-
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
const char *base_name)
{
unsigned int def_conf, conn;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- int idx, err;
+ int err;
bool phantom_jack;
if (!nid)
@@ -482,16 +444,14 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
!is_jack_detectable(codec, nid);
- if (base_name) {
+ if (base_name)
strlcpy(name, base_name, sizeof(name));
- idx = 0;
- } else
- snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
+ else
+ snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL);
if (phantom_jack)
/* Example final name: "Internal Mic Phantom Jack" */
strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
- idx = get_unique_index(codec, name, idx);
- err = __snd_hda_jack_add_kctl(codec, nid, name, idx, phantom_jack);
+ err = __snd_hda_jack_add_kctl(codec, nid, name, phantom_jack);
if (err < 0)
return err;
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index b279e327a23b..387d30984dfe 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -39,11 +39,8 @@ struct hda_jack_tbl {
unsigned int block_report:1; /* in a transitional state - do not report to userspace */
hda_nid_t gating_jack; /* valid when gating jack plugged */
hda_nid_t gated_jack; /* gated is dependent on this jack */
- struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
-#ifdef CONFIG_SND_HDA_INPUT_JACK
int type;
struct snd_jack *jack;
-#endif
};
struct hda_jack_tbl *
@@ -85,7 +82,7 @@ static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
- const char *name, int idx);
+ const char *name);
int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index bed66c314431..4a21c2199e02 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -330,7 +330,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
/*
* generic proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int snd_hda_codec_proc_new(struct hda_codec *codec);
#else
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
@@ -777,7 +777,7 @@ int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size,
bool rev3_or_later);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
struct snd_info_buffer *buffer);
void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 2e4fd5c56d3b..477742cb70a2 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -87,13 +87,13 @@ MODULE_PARM_DESC(power_save,
/*
* DMA page allocation ops.
*/
-static int dma_alloc_pages(struct azx *chip, int type, size_t size,
+static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size,
struct snd_dma_buffer *buf)
{
- return snd_dma_alloc_pages(type, chip->card->dev, size, buf);
+ return snd_dma_alloc_pages(type, bus->dev, size, buf);
}
-static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
+static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
{
snd_dma_free_pages(buf);
}
@@ -102,11 +102,6 @@ static int substream_alloc_pages(struct azx *chip,
struct snd_pcm_substream *substream,
size_t size)
{
- struct azx_dev *azx_dev = get_azx_dev(substream);
-
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
return snd_pcm_lib_malloc_pages(substream, size);
}
@@ -173,7 +168,7 @@ static u8 hda_tegra_readb(u8 *addr)
return (v >> shift) & 0xff;
}
-static const struct hda_controller_ops hda_tegra_ops = {
+static const struct hdac_io_ops hda_tegra_io_ops = {
.reg_writel = hda_tegra_writel,
.reg_readl = hda_tegra_readl,
.reg_writew = hda_tegra_writew,
@@ -182,6 +177,9 @@ static const struct hda_controller_ops hda_tegra_ops = {
.reg_readb = hda_tegra_readb,
.dma_alloc_pages = dma_alloc_pages,
.dma_free_pages = dma_free_pages,
+};
+
+static const struct hda_controller_ops hda_tegra_ops = {
.substream_alloc_pages = substream_alloc_pages,
.substream_free_pages = substream_free_pages,
};
@@ -282,21 +280,29 @@ static const struct dev_pm_ops hda_tegra_pm = {
SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
};
+static int hda_tegra_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+
+ chip->bus.shutdown = 1;
+ return 0;
+}
+
/*
* destructor
*/
static int hda_tegra_dev_free(struct snd_device *device)
{
- int i;
struct azx *chip = device->device_data;
- if (chip->initialized) {
- for (i = 0; i < chip->num_streams; i++)
- azx_stream_stop(chip, &chip->azx_dev[i]);
+ if (azx_bus(chip)->chip_init) {
+ azx_stop_all_streams(chip);
azx_stop_chip(chip);
}
azx_free_stream_pages(chip);
+ azx_free_streams(chip);
+ snd_hdac_bus_exit(azx_bus(chip));
return 0;
}
@@ -304,31 +310,40 @@ static int hda_tegra_dev_free(struct snd_device *device)
static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
{
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct hdac_bus *bus = azx_bus(chip);
struct device *dev = hda->dev;
struct resource *res;
int err;
hda->hda_clk = devm_clk_get(dev, "hda");
- if (IS_ERR(hda->hda_clk))
+ if (IS_ERR(hda->hda_clk)) {
+ dev_err(dev, "failed to get hda clock\n");
return PTR_ERR(hda->hda_clk);
+ }
hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x");
- if (IS_ERR(hda->hda2codec_2x_clk))
+ if (IS_ERR(hda->hda2codec_2x_clk)) {
+ dev_err(dev, "failed to get hda2codec_2x clock\n");
return PTR_ERR(hda->hda2codec_2x_clk);
+ }
hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi");
- if (IS_ERR(hda->hda2hdmi_clk))
+ if (IS_ERR(hda->hda2hdmi_clk)) {
+ dev_err(dev, "failed to get hda2hdmi clock\n");
return PTR_ERR(hda->hda2hdmi_clk);
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hda->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hda->regs))
return PTR_ERR(hda->regs);
- chip->remap_addr = hda->regs + HDA_BAR0;
- chip->addr = res->start + HDA_BAR0;
+ bus->remap_addr = hda->regs + HDA_BAR0;
+ bus->addr = res->start + HDA_BAR0;
err = hda_tegra_enable_clocks(hda);
- if (err)
+ if (err) {
+ dev_err(dev, "failed to get enable clocks\n");
return err;
+ }
hda_tegra_init(hda);
@@ -337,6 +352,7 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
{
+ struct hdac_bus *bus = azx_bus(chip);
struct snd_card *card = chip->card;
int err;
unsigned short gcap;
@@ -354,9 +370,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
irq_id);
return err;
}
- chip->irq = irq_id;
+ bus->irq = irq_id;
- synchronize_irq(chip->irq);
+ synchronize_irq(bus->irq);
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
@@ -374,23 +390,26 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
chip->capture_index_offset = 0;
chip->playback_index_offset = chip->capture_streams;
chip->num_streams = chip->playback_streams + chip->capture_streams;
- chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams,
- sizeof(*chip->azx_dev), GFP_KERNEL);
- if (!chip->azx_dev)
- return -ENOMEM;
- err = azx_alloc_stream_pages(chip);
- if (err < 0)
+ /* initialize streams */
+ err = azx_init_streams(chip);
+ if (err < 0) {
+ dev_err(card->dev, "failed to initialize streams: %d\n", err);
return err;
+ }
- /* initialize streams */
- azx_init_stream(chip);
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0) {
+ dev_err(card->dev, "failed to allocate stream pages: %d\n",
+ err);
+ return err;
+ }
/* initialize chip */
azx_init_chip(chip, 1);
/* codec detection */
- if (!chip->codec_mask) {
+ if (!bus->codec_mask) {
dev_err(card->dev, "no codecs found!\n");
return -ENODEV;
}
@@ -399,7 +418,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
strcpy(card->shortname, "tegra-hda");
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
- card->shortname, chip->addr, chip->irq);
+ card->shortname, bus->addr, bus->irq);
return 0;
}
@@ -409,10 +428,10 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
*/
static int hda_tegra_create(struct snd_card *card,
unsigned int driver_caps,
- const struct hda_controller_ops *hda_ops,
struct hda_tegra *hda)
{
static struct snd_device_ops ops = {
+ .dev_disconnect = hda_tegra_dev_disconnect,
.dev_free = hda_tegra_dev_free,
};
struct azx *chip;
@@ -420,11 +439,9 @@ static int hda_tegra_create(struct snd_card *card,
chip = &hda->chip;
- spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
- chip->ops = hda_ops;
- chip->irq = -1;
+ chip->ops = &hda_tegra_ops;
chip->driver_caps = driver_caps;
chip->driver_type = driver_caps & 0xff;
chip->dev_index = 0;
@@ -435,6 +452,10 @@ static int hda_tegra_create(struct snd_card *card,
chip->single_cmd = false;
chip->snoop = true;
+ err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
+ if (err < 0)
+ return err;
+
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
dev_err(card->dev, "Error creating device\n");
@@ -452,11 +473,12 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match);
static int hda_tegra_probe(struct platform_device *pdev)
{
+ const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY |
+ AZX_DCAPS_CORBRP_SELF_CLEAR;
struct snd_card *card;
struct azx *chip;
struct hda_tegra *hda;
int err;
- const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
if (!hda)
@@ -471,7 +493,7 @@ static int hda_tegra_probe(struct platform_device *pdev)
return err;
}
- err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda);
+ err = hda_tegra_create(card, driver_flags, hda);
if (err < 0)
goto out_free;
card->private_data = chip;
@@ -483,10 +505,6 @@ static int hda_tegra_probe(struct platform_device *pdev)
goto out_free;
/* create codec instances */
- err = azx_bus_create(chip, NULL);
- if (err < 0)
- goto out_free;
-
err = azx_probe_codecs(chip, 0);
if (err < 0)
goto out_free;
@@ -500,7 +518,7 @@ static int hda_tegra_probe(struct platform_device *pdev)
goto out_free;
chip->running = 1;
- snd_hda_set_power_save(chip->bus, power_save * 1000);
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
return 0;
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 231f89029779..c033a4ee6547 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -205,8 +205,6 @@ static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)
if (err < 0)
return err;
- codec->patch_ops = ad198x_auto_patch_ops;
-
return 0;
}
@@ -223,6 +221,7 @@ static int alloc_ad_spec(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
snd_hda_gen_spec_init(&spec->gen);
+ codec->patch_ops = ad198x_auto_patch_ops;
return 0;
}
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 447302695195..484bbf4134cd 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -63,6 +63,7 @@ static int patch_ca0110(struct hda_codec *codec)
return -ENOMEM;
snd_hda_gen_spec_init(spec);
codec->spec = spec;
+ codec->patch_ops = ca0110_patch_ops;
spec->multi_cap_vol = 1;
codec->bus->needs_damn_long_delay = 1;
@@ -71,8 +72,6 @@ static int patch_ca0110(struct hda_codec *codec)
if (err < 0)
goto error;
- codec->patch_ops = ca0110_patch_ops;
-
return 0;
error:
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 4a4e7b282e4f..0f039abe9673 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -43,8 +43,6 @@
#define FLOAT_TWO 0x40000000
#define FLOAT_MINUS_5 0xc0a00000
-#define UNSOL_TAG_HP 0x10
-#define UNSOL_TAG_AMIC1 0x12
#define UNSOL_TAG_DSP 0x16
#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
@@ -703,8 +701,8 @@ struct ca0132_spec {
unsigned int num_mixers;
const struct hda_verb *base_init_verbs;
const struct hda_verb *base_exit_verbs;
- const struct hda_verb *init_verbs[5];
- unsigned int num_init_verbs; /* exclude base init verbs */
+ const struct hda_verb *chip_init_verbs;
+ struct hda_verb *spec_init_verbs;
struct auto_pin_cfg autocfg;
/* Nodes configurations */
@@ -719,6 +717,8 @@ struct ca0132_spec {
unsigned int num_inputs;
hda_nid_t shared_mic_nid;
hda_nid_t shared_out_nid;
+ hda_nid_t unsol_tag_hp;
+ hda_nid_t unsol_tag_amic1;
/* chip access */
struct mutex chipio_mutex; /* chip access mutex */
@@ -748,6 +748,7 @@ struct ca0132_spec {
struct hda_codec *codec;
struct delayed_work unsol_hp_work;
+ int quirk;
#ifdef ENABLE_TUNING_CONTROLS
long cur_ctl_vals[TUNING_CTLS_COUNT];
@@ -755,6 +756,19 @@ struct ca0132_spec {
};
/*
+ * CA0132 quirks table
+ */
+enum {
+ QUIRK_NONE,
+ QUIRK_ALIENWARE,
+};
+
+static const struct snd_pci_quirk ca0132_quirks[] = {
+ SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15", QUIRK_ALIENWARE),
+ {}
+};
+
+/*
* CA0132 codec access
*/
static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
@@ -2052,11 +2066,8 @@ static int dma_convert_to_hda_format(struct hda_codec *codec,
{
unsigned int format_val;
- format_val = snd_hda_calc_stream_format(codec,
- sample_rate,
- channels,
- SNDRV_PCM_FORMAT_S32_LE,
- 32, 0);
+ format_val = snd_hdac_calc_stream_format(sample_rate,
+ channels, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
if (hda_format)
*hda_format = (unsigned short)format_val;
@@ -3227,7 +3238,7 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work)
struct hda_jack_tbl *jack;
ca0132_select_out(spec->codec);
- jack = snd_hda_jack_tbl_get(spec->codec, UNSOL_TAG_HP);
+ jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
if (jack) {
jack->block_report = 0;
snd_hda_jack_report_sync(spec->codec);
@@ -4417,8 +4428,9 @@ static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
static void ca0132_init_unsol(struct hda_codec *codec)
{
- snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_HP, hp_callback);
- snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_AMIC1,
+ struct ca0132_spec *spec = codec->spec;
+ snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback);
+ snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_amic1,
amic_callback);
snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
ca0132_process_dsp_response);
@@ -4479,17 +4491,6 @@ static struct hda_verb ca0132_init_verbs0[] = {
{}
};
-static struct hda_verb ca0132_init_verbs1[] = {
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_HP},
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_AMIC1},
- /* config EAPD */
- {0x0b, 0x78D, 0x00},
- /*{0x0b, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
- /*{0x10, 0x78D, 0x02},*/
- /*{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
- {}
-};
-
static void ca0132_init_chip(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
@@ -4569,8 +4570,8 @@ static int ca0132_init(struct hda_codec *codec)
init_input(codec, cfg->dig_in_pin, spec->dig_in);
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->spec_init_verbs);
ca0132_select_out(codec);
ca0132_select_mic(codec);
@@ -4591,6 +4592,7 @@ static void ca0132_free(struct hda_codec *codec)
snd_hda_sequence_write(codec, spec->base_exit_verbs);
ca0132_exit_chip(codec);
snd_hda_power_down(codec);
+ kfree(spec->spec_init_verbs);
kfree(codec->spec);
}
@@ -4617,18 +4619,25 @@ static void ca0132_config(struct hda_codec *codec)
spec->num_outputs = 2;
spec->out_pins[0] = 0x0b; /* speaker out */
- spec->out_pins[1] = 0x10; /* headphone out */
+ if (spec->quirk == QUIRK_ALIENWARE) {
+ codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n");
+ spec->out_pins[1] = 0x0f;
+ } else{
+ spec->out_pins[1] = 0x10; /* headphone out */
+ }
spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
- spec->num_inputs = 3;
spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
spec->adcs[1] = 0x8; /* analog mic2 */
spec->adcs[2] = 0xa; /* what u hear */
- spec->shared_mic_nid = 0x7;
+ 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;
@@ -4641,10 +4650,56 @@ static void ca0132_config(struct hda_codec *codec)
cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
}
+static int ca0132_prepare_verbs(struct hda_codec *codec)
+{
+/* Verbs + terminator (an empty element) */
+#define NUM_SPEC_VERBS 4
+ struct ca0132_spec *spec = codec->spec;
+
+ spec->chip_init_verbs = ca0132_init_verbs0;
+ spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL);
+ if (!spec->spec_init_verbs)
+ return -ENOMEM;
+
+ /* HP jack autodetection */
+ spec->spec_init_verbs[0].nid = spec->unsol_tag_hp;
+ spec->spec_init_verbs[0].param = AC_VERB_SET_UNSOLICITED_ENABLE;
+ spec->spec_init_verbs[0].verb = AC_USRSP_EN | spec->unsol_tag_hp;
+
+ /* MIC1 jack autodetection */
+ spec->spec_init_verbs[1].nid = spec->unsol_tag_amic1;
+ spec->spec_init_verbs[1].param = AC_VERB_SET_UNSOLICITED_ENABLE;
+ spec->spec_init_verbs[1].verb = AC_USRSP_EN | spec->unsol_tag_amic1;
+
+ /* config EAPD */
+ spec->spec_init_verbs[2].nid = 0x0b;
+ spec->spec_init_verbs[2].param = 0x78D;
+ spec->spec_init_verbs[2].verb = 0x00;
+
+ /* Previously commented configuration */
+ /*
+ spec->spec_init_verbs[3].nid = 0x0b;
+ spec->spec_init_verbs[3].param = AC_VERB_SET_EAPD_BTLENABLE;
+ spec->spec_init_verbs[3].verb = 0x02;
+
+ spec->spec_init_verbs[4].nid = 0x10;
+ spec->spec_init_verbs[4].param = 0x78D;
+ spec->spec_init_verbs[4].verb = 0x02;
+
+ spec->spec_init_verbs[5].nid = 0x10;
+ spec->spec_init_verbs[5].param = AC_VERB_SET_EAPD_BTLENABLE;
+ spec->spec_init_verbs[5].verb = 0x02;
+ */
+
+ /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */
+ return 0;
+}
+
static int patch_ca0132(struct hda_codec *codec)
{
struct ca0132_spec *spec;
int err;
+ const struct snd_pci_quirk *quirk;
codec_dbg(codec, "patch_ca0132\n");
@@ -4654,15 +4709,23 @@ static int patch_ca0132(struct hda_codec *codec)
codec->spec = spec;
spec->codec = codec;
+ codec->patch_ops = ca0132_patch_ops;
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
+
+ /* Detect codec quirk */
+ quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks);
+ if (quirk)
+ spec->quirk = quirk->value;
+ else
+ spec->quirk = QUIRK_NONE;
+
spec->dsp_state = DSP_DOWNLOAD_INIT;
spec->num_mixers = 1;
spec->mixers[0] = ca0132_mixer;
spec->base_init_verbs = ca0132_base_init_verbs;
spec->base_exit_verbs = ca0132_base_exit_verbs;
- spec->init_verbs[0] = ca0132_init_verbs0;
- spec->init_verbs[1] = ca0132_init_verbs1;
- spec->num_init_verbs = 2;
INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
@@ -4670,13 +4733,13 @@ static int patch_ca0132(struct hda_codec *codec)
ca0132_config(codec);
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ err = ca0132_prepare_verbs(codec);
if (err < 0)
return err;
- codec->patch_ops = ca0132_patch_ops;
- codec->pcm_format_first = 1;
- codec->no_sticky_stream = 1;
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
return 0;
}
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 50e9dd675579..25ccf781fbe7 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -584,6 +584,7 @@ static int patch_cs420x(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
+ codec->patch_ops = cs_patch_ops;
spec->gen.automute_hook = cs_automute;
codec->single_adc_amp = 1;
@@ -595,8 +596,6 @@ static int patch_cs420x(struct hda_codec *codec)
if (err < 0)
goto error;
- codec->patch_ops = cs_patch_ops;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -738,6 +737,7 @@ static int patch_cs4208(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
+ codec->patch_ops = cs_patch_ops;
spec->gen.automute_hook = cs_automute;
/* exclude NID 0x10 (HP) from output volumes due to different steps */
spec->gen.out_vol_mask = 1ULL << 0x10;
@@ -756,8 +756,6 @@ static int patch_cs4208(struct hda_codec *codec)
if (err < 0)
goto error;
- codec->patch_ops = cs_patch_ops;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -1150,6 +1148,7 @@ static int patch_cs4210(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
+ codec->patch_ops = cs421x_patch_ops;
spec->gen.automute_hook = cs_automute;
snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
@@ -1167,8 +1166,6 @@ static int patch_cs4210(struct hda_codec *codec)
if (err < 0)
goto error;
- codec->patch_ops = cs421x_patch_ops;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -1187,11 +1184,12 @@ static int patch_cs4213(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
+ codec->patch_ops = cs421x_patch_ops;
+
err = cs421x_parse_auto_config(codec);
if (err < 0)
goto error;
- codec->patch_ops = cs421x_patch_ops;
return 0;
error:
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 617d9012e78a..f5ed078710f8 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -57,6 +57,7 @@ static int patch_cmi9880(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ codec->patch_ops = cmi_auto_patch_ops;
cfg = &spec->gen.autocfg;
snd_hda_gen_spec_init(&spec->gen);
@@ -67,7 +68,6 @@ static int patch_cmi9880(struct hda_codec *codec)
if (err < 0)
goto error;
- codec->patch_ops = cmi_auto_patch_ops;
return 0;
error:
@@ -86,6 +86,7 @@ static int patch_cmi8888(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ codec->patch_ops = cmi_auto_patch_ops;
cfg = &spec->gen.autocfg;
snd_hda_gen_spec_init(&spec->gen);
@@ -112,7 +113,6 @@ static int patch_cmi8888(struct hda_codec *codec)
}
}
- codec->patch_ops = cmi_auto_patch_ops;
return 0;
error:
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 78b719b5b34d..f788a91b544a 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -850,6 +850,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
return -ENOMEM;
snd_hda_gen_spec_init(&spec->gen);
codec->spec = spec;
+ codec->patch_ops = cx_auto_patch_ops;
cx_auto_parse_beep(codec);
cx_auto_parse_eapd(codec);
@@ -908,8 +909,6 @@ static int patch_conexant_auto(struct hda_codec *codec)
if (err < 0)
goto error;
- codec->patch_ops = cx_auto_patch_ops;
-
/* Some laptops with Conexant chips show stalls in S3 resume,
* which falls into the single-cmd mode.
* Better to make reset, then.
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 5f44f60a6389..f8527342a150 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -86,7 +86,7 @@ struct hdmi_spec_per_pin {
bool non_pcm;
bool chmap_set; /* channel-map override by ALSA API? */
unsigned char chmap[8]; /* ALSA API channel-map */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
struct snd_info_entry *proc_entry;
#endif
};
@@ -548,7 +548,7 @@ static void hdmi_set_channel_count(struct hda_codec *codec,
* ELD proc files
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void print_eld_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -592,7 +592,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
{
if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
- snd_device_free(per_pin->codec->card, per_pin->proc_entry);
+ snd_info_free_entry(per_pin->proc_entry);
per_pin->proc_entry = NULL;
}
}
@@ -2049,9 +2049,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
- struct hdmi_spec_per_pin *per_pin;
- per_pin = get_pin(spec, pin_idx);
info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
if (!info)
return -ENOMEM;
@@ -2081,7 +2079,7 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
strncat(hdmi_str, " Phantom",
sizeof(hdmi_str) - strlen(hdmi_str) - 1);
- return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0);
+ return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str);
}
static int generic_hdmi_build_controls(struct hda_codec *codec)
@@ -2335,6 +2333,15 @@ static int patch_generic_hdmi(struct hda_codec *codec)
intel_haswell_fixup_enable_dp12(codec);
}
+ /* For Valleyview/Cherryview, only the display codec is in the display
+ * power well and can use link_power ops to request/release the power.
+ * For Haswell/Broadwell, the controller is also in the power well and
+ * can cover the codec power request, and so need not set this flag.
+ * For previous platforms, there is no such power well feature.
+ */
+ if (is_valleyview_plus(codec) || is_skylake(codec))
+ codec->core.link_power_control = 1;
+
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
codec->depop_delay = 0;
@@ -2349,6 +2356,10 @@ static int patch_generic_hdmi(struct hda_codec *codec)
codec->dp_mst = true;
}
+ /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
+ if (is_haswell_plus(codec) || is_valleyview_plus(codec))
+ codec->auto_runtime_pm = 1;
+
generic_hdmi_init_per_pins(codec);
init_channel_allocations();
@@ -2923,6 +2934,171 @@ static int patch_nvhdmi(struct hda_codec *codec)
}
/*
+ * The HDA codec on NVIDIA Tegra contains two scratch registers that are
+ * accessed using vendor-defined verbs. These registers can be used for
+ * interoperability between the HDA and HDMI drivers.
+ */
+
+/* Audio Function Group node */
+#define NVIDIA_AFG_NID 0x01
+
+/*
+ * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
+ * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
+ * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
+ * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
+ * additional bit (at position 30) to signal the validity of the format.
+ *
+ * | 31 | 30 | 29 16 | 15 0 |
+ * +---------+-------+--------+--------+
+ * | TRIGGER | VALID | UNUSED | FORMAT |
+ * +-----------------------------------|
+ *
+ * Note that for the trigger bit to take effect it needs to change value
+ * (i.e. it needs to be toggled).
+ */
+#define NVIDIA_GET_SCRATCH0 0xfa6
+#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7
+#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8
+#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9
+#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa
+#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
+#define NVIDIA_SCRATCH_VALID (1 << 6)
+
+#define NVIDIA_GET_SCRATCH1 0xfab
+#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac
+#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad
+#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae
+#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf
+
+/*
+ * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
+ * the format is invalidated so that the HDMI codec can be disabled.
+ */
+static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format)
+{
+ unsigned int value;
+
+ /* bits [31:30] contain the trigger and valid bits */
+ value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0,
+ NVIDIA_GET_SCRATCH0, 0);
+ value = (value >> 24) & 0xff;
+
+ /* bits [15:0] are used to store the HDA format */
+ snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+ NVIDIA_SET_SCRATCH0_BYTE0,
+ (format >> 0) & 0xff);
+ snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+ NVIDIA_SET_SCRATCH0_BYTE1,
+ (format >> 8) & 0xff);
+
+ /* bits [16:24] are unused */
+ snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+ NVIDIA_SET_SCRATCH0_BYTE2, 0);
+
+ /*
+ * Bit 30 signals that the data is valid and hence that HDMI audio can
+ * be enabled.
+ */
+ if (format == 0)
+ value &= ~NVIDIA_SCRATCH_VALID;
+ else
+ value |= NVIDIA_SCRATCH_VALID;
+
+ /*
+ * Whenever the trigger bit is toggled, an interrupt is raised in the
+ * HDMI codec. The HDMI driver will use that as trigger to update its
+ * configuration.
+ */
+ value ^= NVIDIA_SCRATCH_TRIGGER;
+
+ snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+ NVIDIA_SET_SCRATCH0_BYTE3, value);
+}
+
+static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int err;
+
+ err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag,
+ format, substream);
+ if (err < 0)
+ return err;
+
+ /* notify the HDMI codec of the format change */
+ tegra_hdmi_set_format(codec, format);
+
+ return 0;
+}
+
+static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ /* invalidate the format in the HDMI codec */
+ tegra_hdmi_set_format(codec, 0);
+
+ return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream);
+}
+
+static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int i;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ struct hda_pcm *pcm = get_pcm_rec(spec, i);
+
+ if (pcm->pcm_type == type)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static int tegra_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm_stream *stream;
+ struct hda_pcm *pcm;
+ int err;
+
+ err = generic_hdmi_build_pcms(codec);
+ if (err < 0)
+ return err;
+
+ pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
+ if (!pcm)
+ return -ENODEV;
+
+ /*
+ * Override ->prepare() and ->cleanup() operations to notify the HDMI
+ * codec about format changes.
+ */
+ stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ stream->ops.prepare = tegra_hdmi_pcm_prepare;
+ stream->ops.cleanup = tegra_hdmi_pcm_cleanup;
+
+ return 0;
+}
+
+static int patch_tegra_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ err = patch_generic_hdmi(codec);
+ if (err)
+ return err;
+
+ codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
+
+ return 0;
+}
+
+/*
* ATI/AMD-specific implementations
*/
@@ -3321,7 +3497,10 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi },
+{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi },
+{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi },
+{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi },
+{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi },
{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 6d010452c1f5..431a20b17df4 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1003,6 +1003,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
codec->single_adc_amp = 1;
/* FIXME: do we need this for all Realtek codec models? */
codec->spdif_status_reset = 1;
+ codec->patch_ops = alc_patch_ops;
err = alc_codec_rename_from_preset(codec);
if (err < 0) {
@@ -1447,6 +1448,8 @@ static int patch_alc880(struct hda_codec *codec)
spec->gen.need_dac_fix = 1;
spec->gen.beep_nid = 0x01;
+ codec->patch_ops.unsol_event = alc880_unsol_event;
+
snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
alc880_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -1459,10 +1462,6 @@ static int patch_alc880(struct hda_codec *codec)
if (!spec->gen.no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- codec->patch_ops = alc_patch_ops;
- codec->patch_ops.unsol_event = alc880_unsol_event;
-
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -1699,6 +1698,8 @@ static int patch_alc260(struct hda_codec *codec)
spec->gen.prefer_hp_amp = 1;
spec->gen.beep_nid = 0x01;
+ spec->shutup = alc_eapd_shutup;
+
snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
alc260_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -1711,9 +1712,6 @@ static int patch_alc260(struct hda_codec *codec)
if (!spec->gen.no_analog)
set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
- codec->patch_ops = alc_patch_ops;
- spec->shutup = alc_eapd_shutup;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -2299,8 +2297,6 @@ static int patch_alc882(struct hda_codec *codec)
if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- codec->patch_ops = alc_patch_ops;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -2436,6 +2432,8 @@ static int patch_alc262(struct hda_codec *codec)
spec = codec->spec;
spec->gen.shared_mic_vref_pin = 0x18;
+ spec->shutup = alc_eapd_shutup;
+
#if 0
/* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
* under-run
@@ -2461,9 +2459,6 @@ static int patch_alc262(struct hda_codec *codec)
if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- codec->patch_ops = alc_patch_ops;
- spec->shutup = alc_eapd_shutup;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -2567,6 +2562,8 @@ static int patch_alc268(struct hda_codec *codec)
spec = codec->spec;
spec->gen.beep_nid = 0x01;
+ spec->shutup = alc_eapd_shutup;
+
snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -2588,9 +2585,6 @@ static int patch_alc268(struct hda_codec *codec)
(0 << AC_AMPCAP_MUTE_SHIFT));
}
- codec->patch_ops = alc_patch_ops;
- spec->shutup = alc_eapd_shutup;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -3586,6 +3580,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
break;
case 0x10ec0286:
case 0x10ec0288:
+ case 0x10ec0298:
alc_process_coef_fw(codec, coef0288);
break;
case 0x10ec0292:
@@ -3660,6 +3655,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
break;
case 0x10ec0286:
case 0x10ec0288:
+ case 0x10ec0298:
alc_update_coef_idx(codec, 0x4f, 0x000c, 0);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
alc_process_coef_fw(codec, coef0288);
@@ -3743,6 +3739,7 @@ static void alc_headset_mode_default(struct hda_codec *codec)
break;
case 0x10ec0286:
case 0x10ec0288:
+ case 0x10ec0298:
alc_process_coef_fw(codec, coef0288);
break;
case 0x10ec0292:
@@ -3807,6 +3804,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
case 0x10ec0283:
alc_process_coef_fw(codec, coef0233);
break;
+ case 0x10ec0298:
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020);/* Headset output enable */
+ /* ALC298 jack type setting is the same with ALC286/ALC288 */
case 0x10ec0286:
case 0x10ec0288:
alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
@@ -3875,6 +3875,9 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
case 0x10ec0283:
alc_process_coef_fw(codec, coef0233);
break;
+ case 0x10ec0298:
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */
+ /* ALC298 jack type setting is the same with ALC286/ALC288 */
case 0x10ec0286:
case 0x10ec0288:
alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400);
@@ -3937,6 +3940,9 @@ static void alc_determine_headset_type(struct hda_codec *codec)
val = alc_read_coef_idx(codec, 0x46);
is_ctia = (val & 0x0070) == 0x0070;
break;
+ case 0x10ec0298:
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); /* Headset output enable */
+ /* ALC298 check jack type is the same with ALC286/ALC288 */
case 0x10ec0286:
case 0x10ec0288:
alc_process_coef_fw(codec, coef0288);
@@ -4517,6 +4523,7 @@ enum {
ALC288_FIXUP_DELL_XPS_13_GPIO6,
ALC292_FIXUP_DELL_E7X,
ALC292_FIXUP_DISABLE_AAMIX,
+ ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5049,6 +5056,16 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC292_FIXUP_DISABLE_AAMIX
},
+ [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5259,6 +5276,8 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
{.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
{.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"},
+ {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"},
+ {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"},
{.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
{.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
{.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
@@ -5317,6 +5336,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x1d, 0x40700001}, \
{0x1e, 0x411111f0}
+#define ALC298_STANDARD_PINS \
+ {0x18, 0x411111f0}, \
+ {0x19, 0x411111f0}, \
+ {0x1a, 0x411111f0}, \
+ {0x1e, 0x411111f0}, \
+ {0x1f, 0x411111f0}
+
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,
@@ -5596,6 +5622,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x16, 0x411111f0},
{0x18, 0x411111f0},
{0x19, 0x411111f0}),
+ 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}),
{}
};
@@ -5654,6 +5688,12 @@ static int patch_alc269(struct hda_codec *codec)
spec->gen.shared_mic_vref_pin = 0x18;
codec->power_save_node = 1;
+#ifdef CONFIG_PM
+ codec->patch_ops.suspend = alc269_suspend;
+ codec->patch_ops.resume = alc269_resume;
+#endif
+ spec->shutup = alc269_shutup;
+
snd_hda_pick_fixup(codec, alc269_fixup_models,
alc269_fixup_tbl, alc269_fixups);
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
@@ -5750,15 +5790,6 @@ static int patch_alc269(struct hda_codec *codec)
if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid)
set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT);
- codec->patch_ops = alc_patch_ops;
- codec->patch_ops.stream_pm = snd_hda_gen_stream_pm;
-#ifdef CONFIG_PM
- codec->patch_ops.suspend = alc269_suspend;
- codec->patch_ops.resume = alc269_resume;
-#endif
- if (!spec->shutup)
- spec->shutup = alc269_shutup;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -5874,6 +5905,10 @@ static int patch_alc861(struct hda_codec *codec)
spec = codec->spec;
spec->gen.beep_nid = 0x23;
+#ifdef CONFIG_PM
+ spec->power_hook = alc_power_eapd;
+#endif
+
snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -5885,11 +5920,6 @@ static int patch_alc861(struct hda_codec *codec)
if (!spec->gen.no_analog)
set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
- codec->patch_ops = alc_patch_ops;
-#ifdef CONFIG_PM
- spec->power_hook = alc_power_eapd;
-#endif
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -5966,6 +5996,8 @@ static int patch_alc861vd(struct hda_codec *codec)
spec = codec->spec;
spec->gen.beep_nid = 0x23;
+ spec->shutup = alc_eapd_shutup;
+
snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -5977,10 +6009,6 @@ static int patch_alc861vd(struct hda_codec *codec)
if (!spec->gen.no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- codec->patch_ops = alc_patch_ops;
-
- spec->shutup = alc_eapd_shutup;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -6581,6 +6609,8 @@ static int patch_alc662(struct hda_codec *codec)
spec = codec->spec;
+ spec->shutup = alc_eapd_shutup;
+
/* handle multiple HPs as is */
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
@@ -6632,9 +6662,6 @@ static int patch_alc662(struct hda_codec *codec)
}
}
- codec->patch_ops = alc_patch_ops;
- spec->shutup = alc_eapd_shutup;
-
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
@@ -6671,8 +6698,6 @@ static int patch_alc680(struct hda_codec *codec)
return err;
}
- codec->patch_ops = alc_patch_ops;
-
return 0;
}
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 6c66d7e16439..dcc7fe91244c 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4362,7 +4362,7 @@ static void stac_shutup(struct hda_codec *codec)
#define stac_free snd_hda_gen_free
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
@@ -4442,6 +4442,7 @@ static int alloc_stac_spec(struct hda_codec *codec)
codec->spec = spec;
codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */
spec->gen.dac_min_mute = true;
+ codec->patch_ops = stac_patch_ops;
return 0;
}
@@ -4458,7 +4459,6 @@ static int patch_stac9200(struct hda_codec *codec)
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
- codec->patch_ops = stac_patch_ops;
codec->power_filter = snd_hda_codec_eapd_power_filter;
snd_hda_add_verbs(codec, stac9200_eapd_init);
@@ -4491,8 +4491,6 @@ static int patch_stac925x(struct hda_codec *codec)
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_add_verbs(codec, stac925x_core_init);
snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl,
@@ -4562,8 +4560,6 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl,
stac92hd73xx_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -4639,8 +4635,6 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
spec->default_polarity = -1; /* no default cfg */
- codec->patch_ops = stac_patch_ops;
-
snd_hda_add_verbs(codec, stac92hd83xxx_core_init);
snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl,
@@ -4689,8 +4683,6 @@ static int patch_stac92hd95(struct hda_codec *codec)
spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids);
spec->default_polarity = 0;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_pick_fixup(codec, stac92hd95_models, stac92hd95_fixup_tbl,
stac92hd95_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -4729,8 +4721,6 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
spec->gen.mixer_nid = 0x17;
spec->have_spdif_mux = 1;
- codec->patch_ops = stac_patch_ops;
-
/* GPIO0 = EAPD */
spec->gpio_mask = 0x01;
spec->gpio_dir = 0x01;
@@ -4809,8 +4799,6 @@ static int patch_stac922x(struct hda_codec *codec)
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_add_verbs(codec, stac922x_core_init);
/* Fix Mux capture level; max to 2 */
@@ -4866,8 +4854,6 @@ static int patch_stac927x(struct hda_codec *codec)
spec->aloopback_shift = 0;
spec->eapd_switch = 1;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl,
stac927x_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -4929,8 +4915,6 @@ static int patch_stac9205(struct hda_codec *codec)
/* Turn on/off EAPD per HP plugging */
spec->eapd_switch = 1;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl,
stac9205_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -5002,8 +4986,6 @@ static int patch_stac9872(struct hda_codec *codec)
spec->linear_tone_beep = 1;
spec->gen.own_eapd_ctl = 1;
- codec->patch_ops = stac_patch_ops;
-
snd_hda_add_verbs(codec, stac9872_core_init);
snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl,
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index bab6c04932aa..0521be8d46a8 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -117,6 +117,8 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream,
int action);
+static const struct hda_codec_ops via_patch_ops; /* defined below */
+
static struct via_spec *via_new_spec(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -137,6 +139,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec)
spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
codec->power_save_node = 1;
spec->gen.power_down_unused = 1;
+ codec->patch_ops = via_patch_ops;
return spec;
}
@@ -481,7 +484,6 @@ static const struct hda_codec_ops via_patch_ops = {
.init = via_init,
.free = via_free,
.unsol_event = snd_hda_jack_unsol_event,
- .stream_pm = snd_hda_gen_stream_pm,
#ifdef CONFIG_PM
.suspend = via_suspend,
.resume = via_resume,
@@ -661,6 +663,9 @@ static int patch_vt1708(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ /* override some patch_ops */
+ codec->patch_ops.build_controls = vt1708_build_controls;
+ codec->patch_ops.build_pcms = vt1708_build_pcms;
spec->gen.mixer_nid = 0x17;
/* set jackpoll_interval while parsing the codec */
@@ -689,10 +694,6 @@ static int patch_vt1708(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
- codec->patch_ops = via_patch_ops;
- codec->patch_ops.build_controls = vt1708_build_controls;
- codec->patch_ops.build_pcms = vt1708_build_pcms;
-
/* clear jackpoll_interval again; it's set dynamically */
codec->jackpoll_interval = 0;
@@ -717,8 +718,6 @@ static int patch_vt1709(struct hda_codec *codec)
return err;
}
- codec->patch_ops = via_patch_ops;
-
return 0;
}
@@ -745,7 +744,6 @@ static int patch_vt1708B(struct hda_codec *codec)
return err;
}
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -810,7 +808,6 @@ static int patch_vt1708S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -852,7 +849,6 @@ static int patch_vt1702(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -925,7 +921,6 @@ static int patch_vt1718S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -1025,7 +1020,6 @@ static int patch_vt1716S(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -1133,7 +1127,6 @@ static int patch_vt2002P(struct hda_codec *codec)
else
spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -1172,7 +1165,6 @@ static int patch_vt1812(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -1210,7 +1202,6 @@ static int patch_vt3476(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
- codec->patch_ops = via_patch_ops;
return 0;
}
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index f7b1523e8a82..8ae3bb7975d1 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2530,8 +2530,8 @@ static int snd_ice1712_create(struct snd_card *card,
if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 28 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
dev_err(card->dev,
"architecture does not support 28bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c
index 6f55e02e5c84..7c387b04067e 100644
--- a/sound/pci/ice1712/quartet.c
+++ b/sound/pci/ice1712/quartet.c
@@ -203,7 +203,6 @@ static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
#define AK4620_DEEMVOL_REG 0x03
#define AK4620_SMUTE (1<<7)
-#ifdef CONFIG_PROC_FS
/*
* Conversion from int value to its binary form. Used for debugging.
* The output buffer must be allocated prior to calling the function.
@@ -228,7 +227,6 @@ static char *get_binary(char *buffer, int value)
buffer[pos] = '\0';
return buffer;
}
-#endif /* CONFIG_PROC_FS */
/*
* Initial setup of the conversion array GPIO <-> rate
@@ -486,7 +484,7 @@ static void set_cpld(struct snd_ice1712 *ice, unsigned int val)
reg_write(ice, GPIO_CPLD_CSN, val);
spec->cpld = val;
}
-#ifdef CONFIG_PROC_FS
+
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -507,9 +505,6 @@ static void proc_init(struct snd_ice1712 *ice)
if (!snd_card_proc_new(ice->card, "quartet", &entry))
snd_info_set_text_ops(entry, ice, proc_regs_read);
}
-#else /* !CONFIG_PROC_FS */
-static void proc_init(struct snd_ice1712 *ice) {}
-#endif
static int qtet_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index b120925223ae..42bcbac801a3 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -2900,7 +2900,6 @@ static int intel8x0_in_clock_list(struct intel8x0 *chip)
return 1;
}
-#ifdef CONFIG_PROC_FS
static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
@@ -2942,9 +2941,6 @@ static void snd_intel8x0_proc_init(struct intel8x0 *chip)
if (! snd_card_proc_new(chip->card, "intel8x0", &entry))
snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read);
}
-#else
-#define snd_intel8x0_proc_init(x)
-#endif
static int snd_intel8x0_dev_free(struct snd_device *device)
{
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 7577f31cd504..1bc98c867133 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1065,7 +1065,6 @@ static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume);
#define INTEL8X0M_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PROC_FS
static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
@@ -1093,10 +1092,6 @@ static void snd_intel8x0m_proc_init(struct intel8x0m *chip)
if (! snd_card_proc_new(chip->card, "intel8x0m", &entry))
snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-#define snd_intel8x0m_proc_init(chip)
-#endif /* CONFIG_PROC_FS */
-
static int snd_intel8x0m_dev_free(struct snd_device *device)
{
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index 601315a1f58f..cba89beb2b38 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -57,13 +57,13 @@ static const char card_name[] = "LX6464ES";
#define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056
static const struct pci_device_id snd_lx6464es_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
- .subvendor = PCI_VENDOR_ID_DIGIGRAM,
- .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM),
}, /* LX6464ES */
- { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
- .subvendor = PCI_VENDOR_ID_DIGIGRAM,
- .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
}, /* LX6464ES-CAE */
{ 0, },
};
@@ -412,9 +412,9 @@ static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
err = snd_pcm_lib_free_pages(substream);
if (is_capture)
- chip->capture_stream.stream = 0;
+ chip->capture_stream.stream = NULL;
else
- chip->playback_stream.stream = 0;
+ chip->playback_stream.stream = NULL;
exit:
mutex_unlock(&chip->setup_mutex);
@@ -981,7 +981,7 @@ static int snd_lx6464es_create(struct snd_card *card,
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- err = pci_set_dma_mask(pci, DMA_BIT_MASK(32));
+ err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
if (err < 0) {
dev_err(card->dev,
"architecture does not support 32bit PCI busmaster DMA\n");
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 9be660993bd0..72e89cedc52d 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2537,8 +2537,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
return -EIO;
/* check, if we can restrict PCI DMA transfers to 28 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) {
dev_err(card->dev,
"architecture does not support 28bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index c3a9f39f8d61..bc81b9f75ed0 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1269,7 +1269,7 @@ static int snd_mixart_probe(struct pci_dev *pci,
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
dev_err(&pci->dev,
"architecture does not support 32bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index ffff3b25fd73..b4ef5804212d 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -196,7 +196,6 @@ static void oxygen_gpio_changed(struct work_struct *work)
chip->model.gpio_changed(chip);
}
-#ifdef CONFIG_PROC_FS
static void oxygen_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -250,9 +249,6 @@ static void oxygen_proc_init(struct oxygen *chip)
if (!snd_card_proc_new(chip->card, "oxygen", &entry))
snd_info_set_text_ops(entry, chip, oxygen_proc_read);
}
-#else
-#define oxygen_proc_init(chip)
-#endif
static const struct pci_device_id *
oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
index 6ce68604c25e..90ac479f389f 100644
--- a/sound/pci/oxygen/xonar_wm87x6.c
+++ b/sound/pci/oxygen/xonar_wm87x6.c
@@ -286,7 +286,7 @@ static void xonar_ds_init(struct oxygen *chip)
xonar_enable_output(chip);
snd_jack_new(chip->card, "Headphone",
- SND_JACK_HEADPHONE, &data->hp_jack);
+ SND_JACK_HEADPHONE, &data->hp_jack, false, false);
xonar_ds_handle_hp_jack(chip);
snd_component_add(chip->card, "WM8776");
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index c6092e48ceb6..9293235281dc 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1537,7 +1537,7 @@ static int pcxhr_probe(struct pci_dev *pci,
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
dev_err(&pci->dev,
"architecture does not support 32bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index efe669b80256..f3860b850210 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -383,9 +383,9 @@ static void __sis_map_silence(struct sis7019 *sis)
{
/* Helper function: must hold sis->voice_lock on entry */
if (!sis->silence_users)
- sis->silence_dma_addr = pci_map_single(sis->pci,
+ sis->silence_dma_addr = dma_map_single(&sis->pci->dev,
sis->suspend_state[0],
- 4096, PCI_DMA_TODEVICE);
+ 4096, DMA_TO_DEVICE);
sis->silence_users++;
}
@@ -394,8 +394,8 @@ static void __sis_unmap_silence(struct sis7019 *sis)
/* Helper function: must hold sis->voice_lock on entry */
sis->silence_users--;
if (!sis->silence_users)
- pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096,
- PCI_DMA_TODEVICE);
+ dma_unmap_single(&sis->pci->dev, sis->silence_dma_addr, 4096,
+ DMA_TO_DEVICE);
}
static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
@@ -1325,7 +1325,7 @@ static int sis_chip_create(struct snd_card *card,
if (rc)
goto error_out;
- rc = pci_set_dma_mask(pci, DMA_BIT_MASK(30));
+ rc = dma_set_mask(&pci->dev, DMA_BIT_MASK(30));
if (rc < 0) {
dev_err(&pci->dev, "architecture does not support 30-bit PCI busmaster DMA");
goto error_out_enabled;
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 0f40624a4275..1b6fad7d4d56 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1259,8 +1259,8 @@ static int snd_sonicvibes_create(struct snd_card *card,
if ((err = pci_enable_device(pci)) < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
dev_err(card->dev,
"architecture does not support 24bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index b72be035f785..599d2b7eb5b8 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -3551,8 +3551,8 @@ int snd_trident_create(struct snd_card *card,
if ((err = pci_enable_device(pci)) < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 30 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(30)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(30)) < 0) {
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(30)) < 0 ||
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(30)) < 0) {
dev_err(card->dev,
"architecture does not support 30bit PCI busmaster DMA\n");
pci_disable_device(pci);
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index 0d1c27e911b8..6120a067494a 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -31,10 +31,15 @@
*/
static struct pmac_keywest *keywest_ctx;
+static bool keywest_probed;
static int keywest_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ keywest_probed = true;
+ /* If instantiated via i2c-powermac, we still need to set the client */
+ if (!keywest_ctx->client)
+ keywest_ctx->client = client;
i2c_set_clientdata(client, keywest_ctx);
return 0;
}
@@ -52,7 +57,7 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
return -EINVAL;
if (strncmp(adapter->name, "mac-io", 6))
- return 0; /* ignored */
+ return -EINVAL; /* ignored */
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "keywest", I2C_NAME_SIZE);
@@ -92,7 +97,8 @@ static int keywest_remove(struct i2c_client *client)
static const struct i2c_device_id keywest_i2c_id[] = {
- { "keywest", 0 },
+ { "MAC,tas3004", 0 }, /* instantiated by i2c-powermac */
+ { "keywest", 0 }, /* instantiated by us if needed */
{ }
};
@@ -100,7 +106,6 @@ static struct i2c_driver keywest_driver = {
.driver = {
.name = "PMac Keywest Audio",
},
- .attach_adapter = keywest_attach_adapter,
.probe = keywest_probe,
.remove = keywest_remove,
.id_table = keywest_i2c_id,
@@ -132,16 +137,37 @@ int snd_pmac_tumbler_post_init(void)
/* exported */
int snd_pmac_keywest_init(struct pmac_keywest *i2c)
{
- int err;
+ struct i2c_adapter *adap;
+ int err, i = 0;
if (keywest_ctx)
return -EBUSY;
+ adap = i2c_get_adapter(0);
+ if (!adap)
+ return -EPROBE_DEFER;
+
keywest_ctx = i2c;
if ((err = i2c_add_driver(&keywest_driver))) {
snd_printk(KERN_ERR "cannot register keywest i2c driver\n");
+ i2c_put_adapter(adap);
return err;
}
- return 0;
+
+ /* There was already a device from i2c-powermac. Great, let's return */
+ if (keywest_probed)
+ return 0;
+
+ /* We assume Macs have consecutive I2C bus numbers starting at 0 */
+ while (adap) {
+ /* Scan for devices to be bound to */
+ err = keywest_attach_adapter(adap);
+ if (!err)
+ return 0;
+ i2c_put_adapter(adap);
+ adap = i2c_get_adapter(++i);
+ }
+
+ return -ENODEV;
}
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3ba52da18bc6..2ae9619443d1 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -45,6 +45,7 @@ source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/intel/Kconfig"
+source "sound/soc/mediatek/Kconfig"
source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/qcom/Kconfig"
@@ -57,6 +58,7 @@ source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"
source "sound/soc/xtensa/Kconfig"
+source "sound/soc/zte/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 974ba708b482..e189903fabf4 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +1,6 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-objs += soc-topology.o
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += intel/
+obj-$(CONFIG_SND_SOC) += mediatek/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/
@@ -38,3 +40,4 @@ obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/
obj-$(CONFIG_SND_SOC) += xtensa/
+obj-$(CONFIG_SND_SOC) += zte/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index e7d08806f3e9..1489cd461aec 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -6,29 +6,35 @@ config SND_ATMEL_SOC
the ATMEL SSC interface. You will also need
to select the audio interfaces to support below.
+if SND_ATMEL_SOC
+
config SND_ATMEL_SOC_PDC
tristate
- depends on SND_ATMEL_SOC
+ default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
+ default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
+
+config SND_ATMEL_SOC_SSC_PDC
+ tristate
config SND_ATMEL_SOC_DMA
tristate
- depends on SND_ATMEL_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
+ default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m
+ default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y)
+
+config SND_ATMEL_SOC_SSC_DMA
+ tristate
config SND_ATMEL_SOC_SSC
tristate
- depends on SND_ATMEL_SOC
- help
- Say Y or M if you want to add support for codecs the
- ATMEL SSC interface. You will also needs to select the individual
- machine drivers to support below.
+ default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y
+ default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
depends on ARCH_AT91 || COMPILE_TEST
- depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI
- select SND_ATMEL_SOC_PDC
- select SND_ATMEL_SOC_SSC
+ depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+ select SND_ATMEL_SOC_SSC_PDC
select SND_SOC_WM8731
help
Say Y if you want to add support for SoC audio on WM8731-based
@@ -37,9 +43,8 @@ config SND_AT91_SOC_SAM9G20_WM8731
config SND_ATMEL_SOC_WM8904
tristate "Atmel ASoC driver for boards using WM8904 codec"
depends on ARCH_AT91 || COMPILE_TEST
- depends on ATMEL_SSC && SND_ATMEL_SOC && I2C
- select SND_ATMEL_SOC_SSC
- select SND_ATMEL_SOC_DMA
+ depends on ATMEL_SSC && I2C
+ select SND_ATMEL_SOC_SSC_DMA
select SND_SOC_WM8904
help
Say Y if you want to add support for Atmel ASoC driver for boards using
@@ -48,10 +53,10 @@ config SND_ATMEL_SOC_WM8904
config SND_AT91_SOC_SAM9X5_WM8731
tristate "SoC Audio support for WM8731-based at91sam9x5 board"
depends on ARCH_AT91 || COMPILE_TEST
- depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI
- select SND_ATMEL_SOC_SSC
- select SND_ATMEL_SOC_DMA
+ depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+ select SND_ATMEL_SOC_SSC_DMA
select SND_SOC_WM8731
help
Say Y if you want to add support for audio SoC on an
at91sam9x5 based board that is using WM8731 codec.
+endif
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index b6625c8c411b..dd57a9eac171 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -124,8 +124,7 @@ static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
int atmel_pcm_dma_platform_register(struct device *dev)
{
- return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+ return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, 0);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 8de836165cf2..d7469cdd90dc 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -95,8 +95,9 @@ static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
static const struct snd_soc_dapm_route intercon[] = {
- /* speaker connected to LHPOUT */
+ /* speaker connected to LHPOUT/RHPOUT */
{"Ext Spk", NULL, "LHPOUT"},
+ {"Ext Spk", NULL, "RHPOUT"},
/* mic is connected to Mic Jack, with WM8731 Mic Bias */
{"MICIN", NULL, "Mic Bias"},
@@ -108,9 +109,7 @@ static const struct snd_soc_dapm_route intercon[] = {
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
printk(KERN_DEBUG
@@ -124,10 +123,6 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- /* not connected */
- snd_soc_dapm_nc_pin(dapm, "RLINEIN");
- snd_soc_dapm_nc_pin(dapm, "LLINEIN");
-
#ifndef ENABLE_MIC_INPUT
snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
#endif
@@ -158,6 +153,7 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
+ .fully_routed = true,
};
static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index c75995f2779c..58c3164802b8 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -21,7 +21,7 @@
#include "../codecs/wm8731.h"
#include "psc.h"
-static struct platform_device_id db1200_pids[] = {
+static const struct platform_device_id db1200_pids[] = {
{
.name = "db1200-ac97",
.driver_data = 0,
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index 5f664471d99e..67a73330db5e 100644
--- a/sound/soc/cirrus/ep93xx-pcm.c
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -60,7 +60,6 @@ int devm_ep93xx_pcm_platform_register(struct device *dev)
{
return devm_snd_dmaengine_pcm_register(dev,
&ep93xx_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index a0f265327fdf..38b3dad9d48a 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1140,7 +1140,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
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) {
/* Enable Audio PLL & Audio section */
data = AUDIO_PLL | AUDIO_SECTION_ON;
pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
@@ -1156,7 +1156,6 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1187,16 +1186,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
.channels_min = 2,
.channels_max = 2,
.rates = PM860X_RATES,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.capture = {
.stream_name = "PCM Capture",
.channels_min = 2,
.channels_max = 2,
.rates = PM860X_RATES,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.ops = &pm860x_pcm_dai_ops,
}, {
@@ -1208,16 +1207,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.capture = {
.stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.ops = &pm860x_i2s_dai_ops,
},
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061c46587628..efaafce8ba38 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,7 +16,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_88PM860X if MFD_88PM860X
select SND_SOC_L3
select SND_SOC_AB8500_CODEC if ABX500_CORE
- select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+ select SND_SOC_AC97_CODEC
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X_SPI if SPI_MASTER
select SND_SOC_AD193X_I2C if I2C
@@ -54,7 +54,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CX20442 if TTY
- select SND_SOC_DA7210 if I2C
+ select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
@@ -104,6 +104,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TAS2552 if I2C
select SND_SOC_TAS5086 if I2C
+ select SND_SOC_TAS571X if I2C
select SND_SOC_TFA9879 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -211,8 +212,9 @@ config SND_SOC_AB8500_CODEC
tristate
config SND_SOC_AC97_CODEC
- tristate
+ tristate "Build generic ASoC AC97 CODEC driver"
select SND_AC97_CODEC
+ select SND_SOC_AC97_BUS
config SND_SOC_AD1836
tristate
@@ -507,6 +509,11 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5670=m
default m if SND_SOC_RT5677=m
+config SND_SOC_RL6347A
+ tristate
+ default y if SND_SOC_RT286=y
+ default m if SND_SOC_RT286=m
+
config SND_SOC_RT286
tristate
depends on I2C
@@ -611,6 +618,10 @@ config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
+config SND_SOC_TAS571X
+ tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
+ depends on I2C
+
config SND_SOC_TFA9879
tristate "NXP Semiconductors TFA9879 amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index abe2d7edf65c..cf160d972cb3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -77,6 +77,7 @@ snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
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-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@@ -106,6 +107,7 @@ snd-soc-sta350-objs := sta350.o
snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-tas5086-objs := tas5086.o
+snd-soc-tas571x-objs := tas571x.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -262,6 +264,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
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_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
@@ -288,6 +291,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.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
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 88ca9cb0ce79..c7d243db010a 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1209,6 +1209,7 @@ static int anc_status_control_put(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 ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
struct device *dev = codec->dev;
bool apply_fir, apply_iir;
@@ -1234,15 +1235,14 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR;
apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR;
- status = snd_soc_dapm_force_enable_pin(&codec->dapm,
- "ANC Configure Input");
+ status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input");
if (status < 0) {
dev_err(dev,
"%s: ERROR: Failed to enable power (status = %d)!\n",
__func__, status);
goto cleanup;
}
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
anc_configure(codec, apply_fir, apply_iir);
@@ -1259,8 +1259,8 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
drvdata->anc_status = ANC_IIR_CONFIGURED;
}
- status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
- snd_soc_dapm_sync(&codec->dapm);
+ status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
+ snd_soc_dapm_sync(dapm);
cleanup:
mutex_unlock(&drvdata->ctrl_lock);
@@ -1947,6 +1947,7 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
struct amic_settings *amics)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
u8 value8;
unsigned int value;
int status;
@@ -1973,15 +1974,15 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__,
amic_micbias_str(amics->mic1a_micbias));
route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias];
- status = snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+ status = snd_soc_dapm_add_routes(dapm, route, 1);
dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__,
amic_micbias_str(amics->mic1b_micbias));
route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias];
- status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+ status |= snd_soc_dapm_add_routes(dapm, route, 1);
dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__,
amic_micbias_str(amics->mic2_micbias));
route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias];
- status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+ status |= snd_soc_dapm_add_routes(dapm, route, 1);
if (status < 0) {
dev_err(codec->dev,
"%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
@@ -2461,6 +2462,7 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
static int ab8500_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct device *dev = codec->dev;
struct device_node *np = dev->of_node;
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
@@ -2541,7 +2543,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
&ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
drvdata->sid_fir_values = (long *)fc->value;
- (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
+ snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
mutex_init(&drvdata->ctrl_lock);
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index d0ac723eee32..5b3224c63943 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -44,10 +44,6 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
}
-#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000)
-
static const struct snd_soc_dai_ops ac97_dai_ops = {
.prepare = ac97_prepare,
};
@@ -58,13 +54,13 @@ static struct snd_soc_dai_driver ac97_dai = {
.stream_name = "AC97 Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.ops = &ac97_dai_ops,
};
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 685998dd086e..95f0bec26a1b 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -251,7 +251,7 @@ static int ad1836_resume(struct snd_soc_codec *codec)
static int ad1836_probe(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int num_dacs, num_adcs;
int ret = 0;
int i;
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 783dcb57043a..a43160254929 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1444,7 +1444,6 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
ADAU1373_PWDN_CTRL3_PWR_EN, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index d4e219b6b98f..ff7f846e3b76 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -101,6 +102,10 @@
#define ADAU1701_FIRMWARE "adau1701.bin"
+static const char * const supply_names[] = {
+ "dvdd", "avdd"
+};
+
struct adau1701 {
int gpio_nreset;
int gpio_pll_mode[2];
@@ -112,6 +117,7 @@ struct adau1701 {
u8 pin_config[12];
struct sigmadsp *sigmadsp;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -565,7 +571,6 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -669,6 +674,13 @@ static int adau1701_probe(struct snd_soc_codec *codec)
if (ret)
return ret;
+ ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
/*
* Let the pll_clkdiv variable default to something that won't happen
* at runtime. That way, we can postpone the firmware download from
@@ -680,7 +692,7 @@ static int adau1701_probe(struct snd_soc_codec *codec)
/* initalize with pre-configured pll mode settings */
ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
if (ret < 0)
- return ret;
+ goto exit_regulators_disable;
/* set up pin config */
val = 0;
@@ -696,10 +708,60 @@ static int adau1701_probe(struct snd_soc_codec *codec)
regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
return 0;
+
+exit_regulators_disable:
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+ return ret;
}
+static int adau1701_remove(struct snd_soc_codec *codec)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(adau1701->gpio_nreset))
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int adau1701_suspend(struct snd_soc_codec *codec)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+
+ return 0;
+}
+
+static int adau1701_resume(struct snd_soc_codec *codec)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ return adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+}
+#else
+#define adau1701_resume NULL
+#define adau1701_suspend NULL
+#endif /* CONFIG_PM */
+
static struct snd_soc_codec_driver adau1701_codec_drv = {
.probe = adau1701_probe,
+ .remove = adau1701_remove,
+ .resume = adau1701_resume,
+ .suspend = adau1701_suspend,
.set_bias_level = adau1701_set_bias_level,
.idle_bias_off = true,
@@ -730,32 +792,58 @@ static int adau1701_i2c_probe(struct i2c_client *client,
struct device *dev = &client->dev;
int gpio_nreset = -EINVAL;
int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
- int ret;
+ int ret, i;
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ adau1701->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
adau1701->client = client;
adau1701->regmap = devm_regmap_init(dev, NULL, client,
&adau1701_regmap);
- if (IS_ERR(adau1701->regmap))
- return PTR_ERR(adau1701->regmap);
+ if (IS_ERR(adau1701->regmap)) {
+ ret = PTR_ERR(adau1701->regmap);
+ goto exit_regulators_disable;
+ }
+
if (dev->of_node) {
gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
- if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
- return gpio_nreset;
+ if (gpio_nreset < 0 && gpio_nreset != -ENOENT) {
+ ret = gpio_nreset;
+ goto exit_regulators_disable;
+ }
gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
"adi,pll-mode-gpios", 0);
- if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
- return gpio_pll_mode[0];
+ if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) {
+ ret = gpio_pll_mode[0];
+ goto exit_regulators_disable;
+ }
gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
"adi,pll-mode-gpios", 1);
- if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
- return gpio_pll_mode[1];
+ if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) {
+ ret = gpio_pll_mode[1];
+ goto exit_regulators_disable;
+ }
of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
&adau1701->pll_clkdiv);
@@ -769,7 +857,7 @@ static int adau1701_i2c_probe(struct i2c_client *client,
ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
"ADAU1701 Reset");
if (ret < 0)
- return ret;
+ goto exit_regulators_disable;
}
if (gpio_is_valid(gpio_pll_mode[0]) &&
@@ -778,13 +866,13 @@ static int adau1701_i2c_probe(struct i2c_client *client,
GPIOF_OUT_INIT_LOW,
"ADAU1701 PLL mode 0");
if (ret < 0)
- return ret;
+ goto exit_regulators_disable;
ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
GPIOF_OUT_INIT_LOW,
"ADAU1701 PLL mode 1");
if (ret < 0)
- return ret;
+ goto exit_regulators_disable;
}
adau1701->gpio_nreset = gpio_nreset;
@@ -795,11 +883,17 @@ static int adau1701_i2c_probe(struct i2c_client *client,
adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
&adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
- if (IS_ERR(adau1701->sigmadsp))
- return PTR_ERR(adau1701->sigmadsp);
+ if (IS_ERR(adau1701->sigmadsp)) {
+ ret = PTR_ERR(adau1701->sigmadsp);
+ goto exit_regulators_disable;
+ }
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
+
+exit_regulators_disable:
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
return ret;
}
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index a1baeee160f4..2f12477e539e 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -466,7 +466,6 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -483,6 +482,7 @@ static enum adau1761_output_mode adau1761_get_lineout_mode(
static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau1761_platform_data *pdata = codec->dev->platform_data;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
enum adau1761_digmic_jackdet_pin_mode mode;
@@ -515,21 +515,18 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
if (ret)
return ret;
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau1761_no_dmic_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes,
ARRAY_SIZE(adau1761_no_dmic_routes));
if (ret)
return ret;
break;
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
- ret = snd_soc_dapm_new_controls(&codec->dapm,
- adau1761_dmic_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, adau1761_dmic_widgets,
ARRAY_SIZE(adau1761_dmic_widgets));
if (ret)
return ret;
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau1761_dmic_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes,
ARRAY_SIZE(adau1761_dmic_routes));
if (ret)
return ret;
@@ -547,6 +544,7 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct adau1761_platform_data *pdata = codec->dev->platform_data;
enum adau1761_output_mode mode;
@@ -577,12 +575,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
}
if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
+ ret = snd_soc_dapm_new_controls(dapm,
adau1761_capless_dapm_widgets,
ARRAY_SIZE(adau1761_capless_dapm_widgets));
if (ret)
return ret;
- ret = snd_soc_dapm_add_routes(&codec->dapm,
+ ret = snd_soc_dapm_add_routes(dapm,
adau1761_capless_dapm_routes,
ARRAY_SIZE(adau1761_capless_dapm_routes));
} else {
@@ -590,12 +588,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
ARRAY_SIZE(adau1761_mono_controls));
if (ret)
return ret;
- ret = snd_soc_dapm_new_controls(&codec->dapm,
+ ret = snd_soc_dapm_new_controls(dapm,
adau1761_mono_dapm_widgets,
ARRAY_SIZE(adau1761_mono_dapm_widgets));
if (ret)
return ret;
- ret = snd_soc_dapm_add_routes(&codec->dapm,
+ ret = snd_soc_dapm_add_routes(dapm,
adau1761_mono_dapm_routes,
ARRAY_SIZE(adau1761_mono_dapm_routes));
}
@@ -640,6 +638,7 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
static int adau1761_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau1761_platform_data *pdata = codec->dev->platform_data;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
@@ -692,14 +691,12 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
return ret;
if (adau->type == ADAU1761) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
- adau1761_dapm_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
ARRAY_SIZE(adau1761_dapm_widgets));
if (ret)
return ret;
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau1761_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes,
ARRAY_SIZE(adau1761_dapm_routes));
if (ret)
return ret;
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index 35581f43fa6d..fde9068550a6 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -339,7 +339,6 @@ static int adau1781_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -383,6 +382,7 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
static int adau1781_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
@@ -403,19 +403,17 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
}
if (pdata && pdata->use_dmic) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
+ ret = snd_soc_dapm_new_controls(dapm,
adau1781_dmic_dapm_widgets,
ARRAY_SIZE(adau1781_dmic_dapm_widgets));
if (ret)
return ret;
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau1781_dmic_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes,
ARRAY_SIZE(adau1781_dmic_dapm_routes));
if (ret)
return ret;
} else {
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau1781_adc_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes,
ARRAY_SIZE(adau1781_adc_dapm_routes));
if (ret)
return ret;
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fa2e690e51c8..fcf05b254ecd 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -155,6 +155,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update update;
@@ -188,7 +189,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
update.reg = reg;
update.val = val;
- snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol,
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
ucontrol->value.enumerated.item[0], e, &update);
}
@@ -444,8 +445,8 @@ static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
- struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
switch (clk_id) {
case ADAU17X1_CLK_SRC_MCLK:
@@ -804,6 +805,7 @@ EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
int adau17x1_add_widgets(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
@@ -811,14 +813,13 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
ARRAY_SIZE(adau17x1_controls));
if (ret)
return ret;
- ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, adau17x1_dapm_widgets,
ARRAY_SIZE(adau17x1_dapm_widgets));
if (ret)
return ret;
if (adau17x1_has_dsp(adau)) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
- adau17x1_dsp_dapm_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets,
ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
if (ret)
return ret;
@@ -840,21 +841,20 @@ EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
int adau17x1_add_routes(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes,
ARRAY_SIZE(adau17x1_dapm_routes));
if (ret)
return ret;
if (adau17x1_has_dsp(adau)) {
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau17x1_dsp_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_dsp_dapm_routes));
} else {
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau17x1_no_dsp_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
}
return ret;
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index 7ad8e156e2df..9bdd15f408c1 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -202,7 +202,7 @@ static const struct snd_soc_dapm_route adau1977_dapm_routes[] = {
ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0)
#define ADAU1977_DC_SUB_SWITCH(x) \
- SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \
+ SOC_SINGLE("ADC" #x " DC Subtraction Capture Switch", \
ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0)
static const struct snd_kcontrol_new adau1977_snd_controls[] = {
@@ -485,7 +485,7 @@ static int adau1977_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)
ret = adau1977_power_enable(adau1977);
break;
case SND_SOC_BIAS_OFF:
@@ -493,12 +493,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (ret)
- return ret;
-
- codec->dapm.bias_level = level;
-
- return 0;
+ return ret;
}
static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -853,12 +848,13 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec,
static int adau1977_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (adau1977->type) {
case ADAU1977:
- ret = snd_soc_dapm_new_controls(&codec->dapm,
+ ret = snd_soc_dapm_new_controls(dapm,
adau1977_micbias_dapm_widgets,
ARRAY_SIZE(adau1977_micbias_dapm_widgets));
if (ret < 0)
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 4373ada95648..36d842570745 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -539,7 +539,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
unsigned int freq, int dir)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
if (dir == SND_SOC_CLOCK_IN) {
switch (clk_id) {
@@ -622,6 +622,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int pll_ctrl1 = 0;
unsigned int pll_ctrl2 = 0;
@@ -687,7 +688,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
adav80x->pll_src = source;
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
}
return 0;
@@ -714,7 +715,6 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -801,11 +801,12 @@ static struct snd_soc_dai_driver adav80x_dais[] = {
static int adav80x_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
/* Force PLLs on for SYSCLK output */
- snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+ snd_soc_dapm_force_enable_pin(dapm, "PLL1");
+ snd_soc_dapm_force_enable_pin(dapm, "PLL2");
/* Power down S/PDIF receiver, since it is currently not supported */
regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20);
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 9130d916f2f4..8670861e5bec 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -341,7 +341,6 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 81b54a270bd8..2d0ff4595ea0 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -412,7 +412,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
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 (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
@@ -439,7 +439,6 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
regcache_mark_dirty(ak4641->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 13585e88f597..7c0f6552c229 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -482,7 +482,6 @@ static int ak4642_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 2a58b1dccd2f..0e59063aeb6f 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -577,7 +577,6 @@ static int ak4671_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index 0e357996864b..0fc24e0d518c 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -826,7 +826,6 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -894,7 +893,7 @@ static int alc5623_resume(struct snd_soc_codec *codec)
static int alc5623_probe(struct snd_soc_codec *codec)
{
struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
alc5623_reset(codec);
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index db3283abbe18..607a63b9705f 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1000,7 +1000,6 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec,
ALC5632_PWR_MANAG_ADD1_MASK, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index eff4b4d512b7..802e05eae3e9 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -208,11 +208,12 @@ static const struct snd_soc_dapm_widget arizona_spkr =
int arizona_init_spk(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
int ret;
- ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1);
+ ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1);
if (ret != 0)
return ret;
@@ -220,8 +221,7 @@ int arizona_init_spk(struct snd_soc_codec *codec)
case WM8997:
break;
default:
- ret = snd_soc_dapm_new_controls(&codec->dapm,
- &arizona_spkr, 1);
+ ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
if (ret != 0)
return ret;
break;
@@ -258,13 +258,14 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
int arizona_init_mono(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
int i;
for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
if (arizona->pdata.out_mono[i])
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
&arizona_mono_routes[i], 1);
}
@@ -274,6 +275,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
int arizona_init_gpio(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
int i;
@@ -281,23 +283,21 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
switch (arizona->type) {
case WM5110:
case WM8280:
- snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+ snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
break;
default:
break;
}
- snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+ snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(&codec->dapm,
- "DRC1 Signal Activity");
+ snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
break;
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(&codec->dapm,
- "DRC2 Signal Activity");
+ snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
break;
default:
break;
@@ -851,6 +851,134 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_hp_ev);
+static int arizona_dvfs_enable(struct snd_soc_codec *codec)
+{
+ const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000);
+ if (ret) {
+ dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+ ARIZONA_SUBSYS_MAX_FREQ,
+ ARIZONA_SUBSYS_MAX_FREQ);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret);
+ regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int arizona_dvfs_disable(struct snd_soc_codec *codec)
+{
+ const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+ ARIZONA_SUBSYS_MAX_FREQ, 0);
+ if (ret) {
+ dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+ if (ret) {
+ dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ mutex_lock(&priv->dvfs_lock);
+
+ if (!priv->dvfs_cached && !priv->dvfs_reqs) {
+ ret = arizona_dvfs_enable(codec);
+ if (ret)
+ goto err;
+ }
+
+ priv->dvfs_reqs |= flags;
+err:
+ mutex_unlock(&priv->dvfs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_up);
+
+int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int old_reqs;
+ int ret = 0;
+
+ mutex_lock(&priv->dvfs_lock);
+
+ old_reqs = priv->dvfs_reqs;
+ priv->dvfs_reqs &= ~flags;
+
+ if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
+ ret = arizona_dvfs_disable(codec);
+
+ mutex_unlock(&priv->dvfs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_down);
+
+int arizona_dvfs_sysclk_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);
+ int ret = 0;
+
+ mutex_lock(&priv->dvfs_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (priv->dvfs_reqs)
+ ret = arizona_dvfs_enable(codec);
+
+ priv->dvfs_cached = false;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* We must ensure DVFS is disabled before the codec goes into
+ * suspend so that we are never in an illegal state of DVFS
+ * enabled without enough DCVDD
+ */
+ priv->dvfs_cached = true;
+
+ if (priv->dvfs_reqs)
+ ret = arizona_dvfs_disable(codec);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&priv->dvfs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev);
+
+void arizona_init_dvfs(struct arizona_priv *priv)
+{
+ mutex_init(&priv->dvfs_lock);
+}
+EXPORT_SYMBOL_GPL(arizona_init_dvfs);
+
static unsigned int arizona_sysclk_48k_rates[] = {
6144000,
12288000,
@@ -1266,7 +1394,7 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
int base = dai->driver->base;
- int i, sr_val;
+ int i, sr_val, ret;
/*
* We will need to be more flexible than this in future,
@@ -1282,6 +1410,23 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
}
sr_val = i;
+ switch (priv->arizona->type) {
+ case WM5102:
+ case WM8997:
+ if (arizona_sr_vals[sr_val] >= 88200)
+ ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ);
+ else
+ ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ);
+
+ if (ret) {
+ arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
switch (dai_priv->clk) {
case ARIZONA_CLK_SYSCLK:
switch (priv->arizona->type) {
@@ -1474,6 +1619,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
struct snd_soc_dapm_route routes[2];
@@ -1504,15 +1650,15 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
routes[0].source = arizona_dai_clk_str(dai_priv->clk);
routes[1].source = arizona_dai_clk_str(dai_priv->clk);
- snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
routes[0].source = arizona_dai_clk_str(clk_id);
routes[1].source = arizona_dai_clk_str(clk_id);
- snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
dai_priv->clk = clk_id;
- return snd_soc_dapm_sync(&codec->dapm);
+ return snd_soc_dapm_sync(dapm);
}
static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
@@ -2140,6 +2286,33 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
}
EXPORT_SYMBOL_GPL(arizona_set_output_mode);
+static const struct soc_enum arizona_adsp2_rate_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+
+const struct snd_kcontrol_new arizona_adsp2_rate_controls[] = {
+ SOC_ENUM("DSP1 Rate", arizona_adsp2_rate_enum[0]),
+ SOC_ENUM("DSP2 Rate", arizona_adsp2_rate_enum[1]),
+ SOC_ENUM("DSP3 Rate", arizona_adsp2_rate_enum[2]),
+ SOC_ENUM("DSP4 Rate", arizona_adsp2_rate_enum[3]),
+};
+EXPORT_SYMBOL_GPL(arizona_adsp2_rate_controls);
+
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 11ff899b0272..43deb0462309 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -60,6 +60,9 @@
#define ARIZONA_MAX_DAI 6
#define ARIZONA_MAX_ADSP 4
+#define ARIZONA_DVFS_SR1_RQ 0x001
+#define ARIZONA_DVFS_ADSP1_RQ 0x100
+
struct arizona;
struct wm_adsp;
@@ -84,6 +87,10 @@ struct arizona_priv {
unsigned int spk_ena:2;
unsigned int spk_ena_pending:1;
+
+ unsigned int dvfs_reqs;
+ struct mutex dvfs_lock;
+ bool dvfs_cached;
};
#define ARIZONA_NUM_MIXER_INPUTS 103
@@ -107,8 +114,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
arizona_mixer_tlv)
#define ARIZONA_MUX_ENUM_DECL(name, reg) \
- SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \
- arizona_mixer_texts, arizona_mixer_values)
+ SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \
+ name, reg, 0, 0xff, arizona_mixer_texts, arizona_mixer_values)
#define ARIZONA_MUX_CTL_DECL(name) \
const struct snd_kcontrol_new name##_mux = \
@@ -210,6 +217,8 @@ extern const struct soc_enum arizona_ng_hold;
extern const struct soc_enum arizona_in_hpf_cut_enum;
extern const struct soc_enum arizona_in_dmic_osr[];
+extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[];
+
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
@@ -245,6 +254,12 @@ struct arizona_fll {
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
};
+extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
+extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
+extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+extern void arizona_init_dvfs(struct arizona_priv *priv);
+
extern int arizona_init_fll(struct arizona *arizona, int id, int base,
int lock_irq, int ok_irq, struct arizona_fll *fll);
extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index e7238b8904bc..b084ad113e96 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -63,7 +63,7 @@ static int bt_sco_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id bt_sco_driver_ids[] = {
+static const struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
},
@@ -74,9 +74,18 @@ static struct platform_device_id bt_sco_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
+#if defined(CONFIG_OF)
+static const struct of_device_id bt_sco_codec_of_match[] = {
+ { .compatible = "delta,dfbmcs320", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
+#endif
+
static struct platform_driver bt_sco_driver = {
.driver = {
.name = "bt-sco",
+ .of_match_table = of_match_ptr(bt_sco_codec_of_match),
},
.probe = bt_sco_probe,
.remove = bt_sco_remove,
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index d6dedd4eab29..1c895a53001d 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -92,7 +92,6 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 60598b230341..8f40025b7e7c 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index cac48ddf3ba6..d7ec4756e45b 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -503,7 +503,6 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec,
CS4265_PWRCTL_PDN);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 1589e7a881d8..4de52c9957ac 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -897,7 +897,7 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
CS42L52_PWRCTL1_PDN_CODEC, 0);
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) {
regcache_cache_only(cs42l52->regmap, false);
regcache_sync(cs42l52->regmap);
}
@@ -908,7 +908,6 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
regcache_cache_only(cs42l52->regmap, true);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -956,7 +955,7 @@ static void cs42l52_beep_work(struct work_struct *work)
struct cs42l52_private *cs42l52 =
container_of(work, struct cs42l52_private, beep_work);
struct snd_soc_codec *codec = cs42l52->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int i;
int val = 0;
int best = 0;
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index cbc654fe48c7..1e11ba45a79f 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -953,7 +953,7 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
CS42L56_PDN_ALL_MASK, 0);
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) {
regcache_cache_only(cs42l56->regmap, false);
regcache_sync(cs42l56->regmap);
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
@@ -978,7 +978,6 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
cs42l56->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1026,7 +1025,7 @@ static void cs42l56_beep_work(struct work_struct *work)
struct cs42l56_private *cs42l56 =
container_of(work, struct cs42l56_private, beep_work);
struct snd_soc_codec *codec = cs42l56->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int i;
int val = 0;
int best = 0;
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 8ecedba79606..b7853b9d3a60 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1208,7 +1208,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
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) {
regcache_cache_only(cs42l73->regmap, false);
regcache_sync(cs42l73->regmap);
}
@@ -1228,7 +1228,6 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 670ebfe12903..e1d46862e81f 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -380,7 +380,7 @@ EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
{
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
switch (cs42xx8->drvdata->num_adcs) {
case 3:
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 0f334bc1b63c..d6f4abbbf8a7 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -333,7 +333,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY)
+ if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_STANDBY)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -341,7 +341,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
err = regulator_enable(cx20442->por);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE)
+ if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_PREPARE)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -351,8 +351,6 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- if (!err)
- codec->dapm.bias_level = level;
return err;
}
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 9ec577f0edb4..238e48a3a4fe 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1374,7 +1374,7 @@ static int da7213_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) {
/* Enable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN,
@@ -1387,7 +1387,6 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 911c26c705fc..207523686bd5 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1432,7 +1432,7 @@ static int da732x_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) {
/* Init Codec */
snd_soc_write(codec, DA732X_REG_REF1,
DA732X_VMID_FASTCHG);
@@ -1502,8 +1502,6 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index ad19cc56702b..66bb446473b8 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1364,7 +1364,7 @@ static int da9055_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) {
/* Enable VMID reference & master bias */
snd_soc_update_bits(codec, DA9055_REFERENCES,
DA9055_VMID_EN | DA9055_BIAS_EN,
@@ -1377,7 +1377,6 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec,
DA9055_VMID_EN | DA9055_BIAS_EN, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index c5f35a07e8e4..6a091016e0fc 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -536,7 +536,7 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
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) {
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
@@ -566,7 +566,6 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index 3a89ce66d51d..ebd90283c960 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -909,8 +909,6 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 933f4476d76c..9363fdbca9cd 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -258,7 +258,7 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* The only way to clear the suspend flag is to reset the codec */
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
jz4740_codec_wakeup(regmap);
mask = JZ4740_CODEC_1_VREF_DISABLE |
@@ -281,8 +281,6 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index a924bb9d7886..99ffc49aa779 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -23,11 +23,6 @@
#include <sound/soc.h>
#include <sound/tlv.h>
-struct lm4857 {
- struct regmap *regmap;
- uint8_t mode;
-};
-
static const struct reg_default lm4857_default_regs[] = {
{ 0x0, 0x00 },
{ 0x1, 0x00 },
@@ -46,66 +41,33 @@ static const struct reg_default lm4857_default_regs[] = {
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
-static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.integer.value[0] = lm4857->mode;
-
- return 0;
-}
-
-static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
- uint8_t value = ucontrol->value.integer.value[0];
-
- lm4857->mode = value;
-
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6);
-
- return 1;
-}
-
-static int lm4857_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
-{
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F,
- lm4857->mode + 6);
- break;
- case SND_SOC_BIAS_STANDBY:
- regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0);
- break;
- default:
- break;
- }
-
- codec->dapm.bias_level = level;
-
- return 0;
-}
+static const unsigned int lm4857_mode_values[] = {
+ 0,
+ 6,
+ 7,
+ 8,
+ 9,
+};
-static const char *lm4857_mode[] = {
+static const char * const lm4857_mode_texts[] = {
+ "Off",
"Earpiece",
"Loudspeaker",
"Loudspeaker + Headphone",
"Headphone",
};
-static SOC_ENUM_SINGLE_EXT_DECL(lm4857_mode_enum, lm4857_mode);
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum,
+ LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values);
+
+static const struct snd_kcontrol_new lm4857_mode_ctrl =
+ SOC_DAPM_ENUM("Mode", lm4857_mode_enum);
static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl),
+
SND_SOC_DAPM_OUTPUT("LS"),
SND_SOC_DAPM_OUTPUT("HP"),
SND_SOC_DAPM_OUTPUT("EP"),
@@ -127,24 +89,18 @@ static const struct snd_kcontrol_new lm4857_controls[] = {
LM4857_WAKEUP, 1, 0),
SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
LM4857_EPGAIN, 1, 0),
-
- SOC_ENUM_EXT("Mode", lm4857_mode_enum,
- lm4857_get_mode, lm4857_set_mode),
};
-/* There is a demux between the input signal and the output signals.
- * Currently there is no easy way to model it in ASoC and since it does not make
- * much of a difference in practice simply connect the input direclty to the
- * outputs. */
static const struct snd_soc_dapm_route lm4857_routes[] = {
- {"LS", NULL, "IN"},
- {"HP", NULL, "IN"},
- {"EP", NULL, "IN"},
+ { "Mode", NULL, "IN" },
+ { "LS", "Loudspeaker", "Mode" },
+ { "LS", "Loudspeaker + Headphone", "Mode" },
+ { "HP", "Headphone", "Mode" },
+ { "HP", "Loudspeaker + Headphone", "Mode" },
+ { "EP", "Earpiece", "Mode" },
};
-static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
- .set_bias_level = lm4857_set_bias_level,
-
+static struct snd_soc_component_driver lm4857_component_driver = {
.controls = lm4857_controls,
.num_controls = ARRAY_SIZE(lm4857_controls),
.dapm_widgets = lm4857_dapm_widgets,
@@ -167,25 +123,14 @@ static const struct regmap_config lm4857_regmap_config = {
static int lm4857_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct lm4857 *lm4857;
-
- lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL);
- if (!lm4857)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, lm4857);
-
- lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
- if (IS_ERR(lm4857->regmap))
- return PTR_ERR(lm4857->regmap);
+ struct regmap *regmap;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
-}
+ regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
-static int lm4857_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
+ return devm_snd_soc_register_component(&i2c->dev,
+ &lm4857_component_driver, NULL, 0);
}
static const struct i2c_device_id lm4857_i2c_id[] = {
@@ -200,7 +145,6 @@ static struct i2c_driver lm4857_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = lm4857_i2c_probe,
- .remove = lm4857_i2c_remove,
.id_table = lm4857_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index c4dfde9bdf1c..6600aa0a33dc 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1271,7 +1271,7 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
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)
regcache_sync(lm49453->regmap);
snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
@@ -1284,8 +1284,6 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 805b3f8cd39d..d0f45348bfbb 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1571,7 +1571,7 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec,
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)
regcache_sync(max98088->regmap);
snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
@@ -1584,7 +1584,6 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec,
regcache_mark_dirty(max98088->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 3e33ef2acf3c..78268f0514e9 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -27,7 +27,7 @@
#include "max98090.h"
/* Allows for sparsely populated register maps */
-static struct reg_default max98090_reg[] = {
+static const struct reg_default max98090_reg[] = {
{ 0x00, 0x00 }, /* 00 Software Reset */
{ 0x03, 0x04 }, /* 03 Interrupt Masks */
{ 0x04, 0x00 }, /* 04 System Clock Quick */
@@ -1500,7 +1500,7 @@ static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
static int max98090_add_widgets(struct snd_soc_codec *codec)
{
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
snd_soc_add_codec_controls(codec, max98090_snd_controls,
ARRAY_SIZE(max98090_snd_controls));
@@ -1798,16 +1798,17 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
- if (!IS_ERR(max98090->mclk)) {
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- clk_disable_unprepare(max98090->mclk);
- else
- clk_prepare_enable(max98090->mclk);
- }
+ if (IS_ERR(max98090->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
+ clk_disable_unprepare(max98090->mclk);
+ else
+ clk_prepare_enable(max98090->mclk);
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) {
ret = regcache_sync(max98090->regmap);
if (ret != 0) {
dev_err(codec->dev,
@@ -1824,7 +1825,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
regcache_mark_dirty(max98090->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -2187,7 +2187,6 @@ static void max98090_jack_work(struct work_struct *work)
struct max98090_priv,
jack_work.work);
struct snd_soc_codec *codec = max98090->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
int status = 0;
int reg;
@@ -2266,8 +2265,6 @@ static void max98090_jack_work(struct work_struct *work)
snd_soc_jack_report(max98090->jack, status,
SND_JACK_HEADSET | SND_JACK_BTN_0);
-
- snd_soc_dapm_sync(dapm);
}
static irqreturn_t max98090_interrupt(int irq, void *data)
@@ -2422,6 +2419,8 @@ static int max98090_probe(struct snd_soc_codec *codec)
struct max98090_cdata *cdata;
enum max98090_type devtype;
int ret = 0;
+ int err;
+ unsigned int micbias;
dev_dbg(codec->dev, "max98090_probe\n");
@@ -2506,8 +2505,17 @@ static int max98090_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
M98090_VCM_MODE_MASK);
+ err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias);
+ if (err) {
+ micbias = M98090_MBVSEL_2V8;
+ dev_info(codec->dev, "use default 2.8v micbias\n");
+ } else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) {
+ dev_err(codec->dev, "micbias out of range 0x%x\n", micbias);
+ micbias = M98090_MBVSEL_2V8;
+ }
+
snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
- M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
+ M98090_MBVSEL_MASK, micbias);
max98090_add_widgets(codec);
@@ -2696,7 +2704,7 @@ static const struct of_device_id max98090_of_match[] = {
MODULE_DEVICE_TABLE(of, max98090_of_match);
#ifdef CONFIG_ACPI
-static struct acpi_device_id max98090_acpi_match[] = {
+static const struct acpi_device_id max98090_acpi_match[] = {
{ "193C9890", MAX98090 },
{ }
};
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 8fba0c3db798..9a46d3dcf703 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1650,16 +1650,17 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
- if (!IS_ERR(max98095->mclk)) {
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- clk_disable_unprepare(max98095->mclk);
- else
- clk_prepare_enable(max98095->mclk);
- }
+ if (IS_ERR(max98095->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
+ clk_disable_unprepare(max98095->mclk);
+ else
+ clk_prepare_enable(max98095->mclk);
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) {
ret = regcache_sync(max98095->regmap);
if (ret != 0) {
@@ -1678,7 +1679,6 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
regcache_mark_dirty(max98095->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -2198,7 +2198,7 @@ static int max98095_suspend(struct snd_soc_codec *codec)
if (max98095->headphone_jack || max98095->mic_jack)
max98095_jack_detect_disable(codec);
- max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -2208,7 +2208,7 @@ static int max98095_resume(struct snd_soc_codec *codec)
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);
- max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (max98095->headphone_jack || max98095->mic_jack) {
max98095_jack_detect_enable(codec);
@@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
/* register an audio interrupt */
ret = request_threaded_irq(client->irq, NULL,
max98095_report_jack,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "max98095", codec);
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "max98095", codec);
if (ret) {
dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
goto err_access;
diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c
index bf3e933ee895..3a2fda08a893 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -60,13 +60,12 @@ static int max98357a_codec_probe(struct snd_soc_codec *codec)
{
struct gpio_desc *sdmode;
- sdmode = devm_gpiod_get(codec->dev, "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));
return PTR_ERR(sdmode);
}
- gpiod_direction_output(sdmode, 0);
snd_soc_codec_set_drvdata(codec, sdmode);
return 0;
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index 10f8e47ce2c2..481d58f1cb3f 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -252,7 +252,7 @@ static int max9850_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) {
ret = regcache_sync(max9850->regmap);
if (ret) {
dev_err(codec->dev,
@@ -264,7 +264,6 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index 9b5a17de4690..aad664225dc3 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -346,7 +346,7 @@ static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai,
}
regmap_update_bits(max98925->regmap, MAX98925_FORMAT,
- M98925_DAI_BCI_MASK, invert);
+ M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK, invert);
return 0;
}
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 711f55039522..b74118e019fb 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -341,6 +341,7 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
int i = get_coeff(priv->mclk, params_rate(hw_params));
+ int srate;
if (i < 0)
return i;
@@ -370,53 +371,16 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
BIT(0) | BIT(1), 0);
}
- switch (params_rate(hw_params)) {
- case 16000:
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
- get_srate(params_rate(hw_params)));
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
- coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
- coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
- coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
- coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
- coeff_div[i].plldiv);
- break;
- case 32000:
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
- get_srate(params_rate(hw_params)));
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
- coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
- coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
- coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
- coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
- coeff_div[i].plldiv);
- break;
- case 48000:
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
- get_srate(params_rate(hw_params)));
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
- coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
- coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
- coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
- coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
- coeff_div[i].plldiv);
- break;
- default:
- pr_err("%s:this rate is no support for ml26124\n", __func__);
- return -EINVAL;
- }
+ srate = get_srate(params_rate(hw_params));
+ if (srate < 0)
+ return srate;
+
+ snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate);
+ snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, coeff_div[i].pllnl);
+ snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, coeff_div[i].pllnh);
+ snd_soc_update_bits(codec, ML26124_PLLML, 0xff, coeff_div[i].pllml);
+ snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh);
+ snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv);
return 0;
}
@@ -523,7 +487,7 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* VMID ON */
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
ML26124_VMID, ML26124_VMID);
msleep(500);
@@ -536,7 +500,6 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
ML26124_VMID, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index e12764d15431..de16429f0a43 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -242,7 +242,7 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -270,7 +270,7 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -298,7 +298,7 @@ static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -641,8 +641,6 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c
new file mode 100644
index 000000000000..91d5166bd3a1
--- /dev/null
+++ b/sound/soc/codecs/rl6347a.c
@@ -0,0 +1,128 @@
+/*
+ * rl6347a.c - RL6347A class device shared support
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/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/dmi.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/hda_verbs.h>
+
+#include "rl6347a.h"
+
+int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
+{
+ struct i2c_client *client = context;
+ struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
+ u8 data[4];
+ int ret, i;
+
+ /* handle index registers */
+ if (reg <= 0xff) {
+ rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
+ for (i = 0; i < rl6347a->index_cache_size; i++) {
+ if (reg == rl6347a->index_cache[i].reg) {
+ rl6347a->index_cache[i].def = value;
+ break;
+ }
+
+ }
+ reg = RL6347A_PROC_COEF;
+ }
+
+ data[0] = (reg >> 24) & 0xff;
+ data[1] = (reg >> 16) & 0xff;
+ /*
+ * 4 bit VID: reg should be 0
+ * 12 bit VID: value should be 0
+ * So we use an OR operator to handle it rather than use if condition.
+ */
+ data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
+ data[3] = value & 0xff;
+
+ ret = i2c_master_send(client, data, 4);
+
+ if (ret == 4)
+ return 0;
+ else
+ pr_err("ret=%d\n", ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(rl6347a_hw_write);
+
+int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct i2c_msg xfer[2];
+ int ret;
+ __be32 be_reg;
+ unsigned int index, vid, buf = 0x0;
+
+ /* handle index registers */
+ if (reg <= 0xff) {
+ rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
+ reg = RL6347A_PROC_COEF;
+ }
+
+ reg = reg | 0x80000;
+ vid = (reg >> 8) & 0xfff;
+
+ if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
+ index = (reg >> 8) & 0xf;
+ reg = (reg & ~0xf0f) | index;
+ }
+ be_reg = cpu_to_be32(reg);
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 4;
+ xfer[0].buf = (u8 *)&be_reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 4;
+ xfer[1].buf = (u8 *)&buf;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+
+ *value = be32_to_cpu(buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rl6347a_hw_read);
+
+MODULE_DESCRIPTION("RL6347A class device shared support");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h
new file mode 100644
index 000000000000..1cb56e50b7f3
--- /dev/null
+++ b/sound/soc/codecs/rl6347a.h
@@ -0,0 +1,32 @@
+/*
+ * rl6347a.h - RL6347A class device shared support
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __RL6347A_H__
+#define __RL6347A_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RL6347A_VENDOR_REGISTERS 0x20
+
+#define RL6347A_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0)
+#define RL6347A_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0)
+
+struct rl6347a_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+};
+
+int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value);
+int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value);
+
+#endif /* __RL6347A_H__ */
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 0fcda35a3a93..5c43e263b2c1 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -31,12 +31,15 @@
#include <sound/rt286.h>
#include <sound/hda_verbs.h>
+#include "rl6347a.h"
#include "rt286.h"
#define RT286_VENDOR_ID 0x10ec0286
#define RT288_VENDOR_ID 0x10ec0288
struct rt286_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
struct regmap *regmap;
struct snd_soc_codec *codec;
struct rt286_platform_data pdata;
@@ -45,7 +48,6 @@ struct rt286_priv {
struct delayed_work jack_detect_work;
int sys_clk;
int clk_id;
- struct reg_default *index_cache;
};
static struct reg_default rt286_index_def[] = {
@@ -185,94 +187,6 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
}
}
-static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
-{
- struct i2c_client *client = context;
- struct rt286_priv *rt286 = i2c_get_clientdata(client);
- u8 data[4];
- int ret, i;
-
- /* handle index registers */
- if (reg <= 0xff) {
- rt286_hw_write(client, RT286_COEF_INDEX, reg);
- for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- if (reg == rt286->index_cache[i].reg) {
- rt286->index_cache[i].def = value;
- break;
- }
-
- }
- reg = RT286_PROC_COEF;
- }
-
- data[0] = (reg >> 24) & 0xff;
- data[1] = (reg >> 16) & 0xff;
- /*
- * 4 bit VID: reg should be 0
- * 12 bit VID: value should be 0
- * So we use an OR operator to handle it rather than use if condition.
- */
- data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
- data[3] = value & 0xff;
-
- ret = i2c_master_send(client, data, 4);
-
- if (ret == 4)
- return 0;
- else
- pr_err("ret=%d\n", ret);
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
-{
- struct i2c_client *client = context;
- struct i2c_msg xfer[2];
- int ret;
- __be32 be_reg;
- unsigned int index, vid, buf = 0x0;
-
- /* handle index registers */
- if (reg <= 0xff) {
- rt286_hw_write(client, RT286_COEF_INDEX, reg);
- reg = RT286_PROC_COEF;
- }
-
- reg = reg | 0x80000;
- vid = (reg >> 8) & 0xfff;
-
- if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
- index = (reg >> 8) & 0xf;
- reg = (reg & ~0xf0f) | index;
- }
- be_reg = cpu_to_be32(reg);
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 4;
- xfer[0].buf = (u8 *)&be_reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 4;
- xfer[1].buf = (u8 *)&buf;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret < 0)
- return ret;
- else if (ret != 2)
- return -EIO;
-
- *value = be32_to_cpu(buf);
-
- return 0;
-}
-
#ifdef CONFIG_PM
static void rt286_index_sync(struct snd_soc_codec *codec)
{
@@ -301,6 +215,7 @@ static int rt286_support_power_controls[] = {
static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
{
+ struct snd_soc_dapm_context *dapm;
unsigned int val, buf;
*hp = false;
@@ -308,6 +223,9 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
if (!rt286->codec)
return -EINVAL;
+
+ dapm = snd_soc_codec_get_dapm(rt286->codec);
+
if (rt286->pdata.cbj_en) {
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
*hp = buf & 0x80000000;
@@ -316,14 +234,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
regmap_update_bits(rt286->regmap,
RT286_DC_GAIN, 0x200, 0x200);
- snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
- "HV");
- snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
- "VREF");
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
/* power LDO1 */
- snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
- "LDO1");
- snd_soc_dapm_sync(&rt286->codec->dapm);
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
msleep(50);
@@ -360,11 +275,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*mic = buf & 0x80000000;
}
- snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
- snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
if (!*hp)
- snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
- snd_soc_dapm_sync(&rt286->codec->dapm);
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
return 0;
}
@@ -391,6 +306,7 @@ static void rt286_jack_detect_work(struct work_struct *work)
int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
rt286->jack = jack;
@@ -398,7 +314,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
if (jack) {
/* enable IRQ */
if (rt286->jack->status & SND_JACK_HEADPHONE)
- snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1");
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
/* Send an initial empty report */
snd_soc_jack_report(rt286->jack, rt286->jack->status,
@@ -406,9 +322,9 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
} else {
/* disable IRQ */
regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0);
- snd_soc_dapm_disable_pin(&codec->dapm, "LDO1");
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
}
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
return 0;
}
@@ -985,7 +901,7 @@ static int rt286_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_write(codec,
RT286_SET_AUDIO_POWER, AC_PWRST_D0);
snd_soc_update_bits(codec,
@@ -1012,7 +928,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1173,8 +1088,8 @@ static const struct regmap_config rt286_regmap = {
.max_register = 0x02370100,
.volatile_reg = rt286_volatile_register,
.readable_reg = rt286_readable_register,
- .reg_write = rt286_hw_write,
- .reg_read = rt286_hw_read,
+ .reg_write = rl6347a_hw_write,
+ .reg_read = rl6347a_hw_read,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = rt286_reg,
.num_reg_defaults = ARRAY_SIZE(rt286_reg),
@@ -1247,6 +1162,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
}
rt286->index_cache = rt286_index_def;
+ rt286->index_cache_size = INDEX_CACHE_SIZE;
rt286->i2c = i2c;
i2c_set_clientdata(i2c, rt286);
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 2c10d77727af..058167c80d71 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1546,7 +1546,7 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
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) {
snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS,
RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
@@ -1569,7 +1569,6 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1615,7 +1614,7 @@ static int rt5631_probe(struct snd_soc_codec *codec)
RT5631_DMIC_R_CH_LATCH_RISING);
}
- codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+ snd_soc_codec_init_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 178e55d4d481..9bc78e57513d 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 struct reg_default init_list[] = {
+static const struct reg_default init_list[] = {
{RT5640_PR_BASE + 0x3d, 0x3600},
{RT5640_PR_BASE + 0x12, 0x0aa8},
{RT5640_PR_BASE + 0x14, 0x0aaa},
@@ -59,7 +59,6 @@ static struct reg_default init_list[] = {
{RT5640_PR_BASE + 0x21, 0xe0e0},
{RT5640_PR_BASE + 0x23, 0x1804},
};
-#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5640_reg[] = {
{ 0x00, 0x000e },
@@ -1870,7 +1869,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
{
switch (level) {
case SND_SOC_BIAS_STANDBY:
- if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
RT5640_PWR_VREF1 | RT5640_PWR_MB |
RT5640_PWR_BG | RT5640_PWR_VREF2,
@@ -1902,7 +1901,6 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1935,11 +1933,12 @@ EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
static int rt5640_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
rt5640->codec = codec;
- rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
@@ -1951,18 +1950,18 @@ static int rt5640_probe(struct snd_soc_codec *codec)
snd_soc_add_codec_controls(codec,
rt5640_specific_snd_controls,
ARRAY_SIZE(rt5640_specific_snd_controls));
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5640_specific_dapm_widgets,
ARRAY_SIZE(rt5640_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5640_specific_dapm_routes,
ARRAY_SIZE(rt5640_specific_dapm_routes));
break;
case RT5640_ID_5639:
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5639_specific_dapm_widgets,
ARRAY_SIZE(rt5639_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5639_specific_dapm_routes,
ARRAY_SIZE(rt5639_specific_dapm_routes));
break;
@@ -1991,7 +1990,7 @@ static int rt5640_suspend(struct snd_soc_codec *codec)
{
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
- rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
rt5640_reset(codec);
regcache_cache_only(rt5640->regmap, true);
regcache_mark_dirty(rt5640->regmap);
@@ -2122,7 +2121,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match);
#endif
#ifdef CONFIG_ACPI
-static struct acpi_device_id rt5640_acpi_match[] = {
+static const struct acpi_device_id rt5640_acpi_match[] = {
{ "INT33CA", 0 },
{ "10EC5640", 0 },
{ "10EC5642", 0 },
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index be4d741c45ba..9ce311e088fc 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -18,7 +18,9 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -347,6 +349,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
case RT5645_TDM_CTRL_1:
case RT5645_TDM_CTRL_2:
case RT5645_TDM_CTRL_3:
+ case RT5650_TDM_CTRL_4:
case RT5645_GLB_CLK:
case RT5645_PLL_CTRL1:
case RT5645_PLL_CTRL2:
@@ -415,9 +418,9 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
}
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+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 */
@@ -432,30 +435,6 @@ static unsigned int bst_tlv[] = {
8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
};
-static const char * const rt5645_tdm_data_swap_select[] = {
- "L/R", "R/L", "L/L", "R/R"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
- RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select);
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
- RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select);
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
- RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select);
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum,
- RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select);
-
-static const char * const rt5645_tdm_adc_data_select[] = {
- "1/2/R", "2/1/R", "R/1/2", "R/2/1"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum,
- RT5645_TDM_CTRL_1, 8,
- rt5645_tdm_adc_data_select);
-
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -464,9 +443,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
/* Headphone Output Volume */
- SOC_DOUBLE("HP Channel Switch", RT5645_HP_VOL,
+ SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
- SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL,
+ SOC_DOUBLE_TLV("Headphone Playback Volume", RT5645_HP_VOL,
RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
/* OUTPUT Control */
@@ -481,9 +460,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL,
RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1),
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
@@ -499,11 +478,11 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL,
RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL,
RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
/* ADC Boost Volume Control */
SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1,
@@ -516,17 +495,6 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* I2S2 function select */
SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
1, 1),
-
- /* TDM */
- SOC_ENUM("TDM Adc Slot0 1 Data", rt5645_tdm_adc_slot0_1_enum),
- SOC_ENUM("TDM Adc Slot2 3 Data", rt5645_tdm_adc_slot2_3_enum),
- SOC_ENUM("TDM Adc Slot4 5 Data", rt5645_tdm_adc_slot4_5_enum),
- SOC_ENUM("TDM Adc Slot6 7 Data", rt5645_tdm_adc_slot6_7_enum),
- SOC_ENUM("TDM IF1 ADC DATA Sel", rt5645_tdm_adc_sel_enum),
- SOC_SINGLE("TDM IF1_DAC1_L Sel", RT5645_TDM_CTRL_3, 12, 7, 0),
- SOC_SINGLE("TDM IF1_DAC1_R Sel", RT5645_TDM_CTRL_3, 8, 7, 0),
- SOC_SINGLE("TDM IF1_DAC2_L Sel", RT5645_TDM_CTRL_3, 4, 7, 0),
- SOC_SINGLE("TDM IF1_DAC2_R Sel", RT5645_TDM_CTRL_3, 0, 7, 0),
};
/**
@@ -1093,7 +1061,8 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux =
/* MX-77 [9:8] */
static const char * const rt5645_if1_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "VAD_ADC"
+ "IF_ADC1/IF_ADC2/VAD_ADC", "IF_ADC2/IF_ADC1/VAD_ADC",
+ "VAD_ADC/IF_ADC1/IF_ADC2", "VAD_ADC/IF_ADC2/IF_ADC1"
};
static SOC_ENUM_SINGLE_DECL(
@@ -1103,6 +1072,140 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_if1_adc_in_mux =
SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum);
+/* MX-78 [4:0] */
+static const char * const rt5650_if1_adc_in_src[] = {
+ "IF_ADC1/IF_ADC2/DAC_REF/Null",
+ "IF_ADC1/IF_ADC2/Null/DAC_REF",
+ "IF_ADC1/DAC_REF/IF_ADC2/Null",
+ "IF_ADC1/DAC_REF/Null/IF_ADC2",
+ "IF_ADC1/Null/DAC_REF/IF_ADC2",
+ "IF_ADC1/Null/IF_ADC2/DAC_REF",
+
+ "IF_ADC2/IF_ADC1/DAC_REF/Null",
+ "IF_ADC2/IF_ADC1/Null/DAC_REF",
+ "IF_ADC2/DAC_REF/IF_ADC1/Null",
+ "IF_ADC2/DAC_REF/Null/IF_ADC1",
+ "IF_ADC2/Null/DAC_REF/IF_ADC1",
+ "IF_ADC2/Null/IF_ADC1/DAC_REF",
+
+ "DAC_REF/IF_ADC1/IF_ADC2/Null",
+ "DAC_REF/IF_ADC1/Null/IF_ADC2",
+ "DAC_REF/IF_ADC2/IF_ADC1/Null",
+ "DAC_REF/IF_ADC2/Null/IF_ADC1",
+ "DAC_REF/Null/IF_ADC1/IF_ADC2",
+ "DAC_REF/Null/IF_ADC2/IF_ADC1",
+
+ "Null/IF_ADC1/IF_ADC2/DAC_REF",
+ "Null/IF_ADC1/DAC_REF/IF_ADC2",
+ "Null/IF_ADC2/IF_ADC1/DAC_REF",
+ "Null/IF_ADC2/DAC_REF/IF_ADC1",
+ "Null/DAC_REF/IF_ADC1/IF_ADC2",
+ "Null/DAC_REF/IF_ADC2/IF_ADC1",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_if1_adc_in_enum, RT5645_TDM_CTRL_2,
+ 0, rt5650_if1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5650_if1_adc_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC IN source", rt5650_if1_adc_in_enum);
+
+/* MX-78 [15:14][13:12][11:10] */
+static const char * const rt5645_tdm_adc_swap_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot0_1_enum,
+ RT5645_TDM_CTRL_2, 14, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc1_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5650_tdm_adc_slot0_1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot2_3_enum,
+ RT5645_TDM_CTRL_2, 12, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc2_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5650_tdm_adc_slot2_3_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot4_5_enum,
+ RT5645_TDM_CTRL_2, 10, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc3_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5650_tdm_adc_slot4_5_enum);
+
+/* MX-77 [7:6][5:4][3:2] */
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
+ RT5645_TDM_CTRL_1, 6, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc1_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5645_tdm_adc_slot0_1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
+ RT5645_TDM_CTRL_1, 4, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc2_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5645_tdm_adc_slot2_3_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
+ RT5645_TDM_CTRL_1, 2, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc3_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5645_tdm_adc_slot4_5_enum);
+
+/* MX-79 [14:12][10:8][6:4][2:0] */
+static const char * const rt5645_tdm_dac_swap_select[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac0_enum,
+ RT5645_TDM_CTRL_3, 12, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac0_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC0 source", rt5645_tdm_dac0_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac1_enum,
+ RT5645_TDM_CTRL_3, 8, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac1_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC1 source", rt5645_tdm_dac1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac2_enum,
+ RT5645_TDM_CTRL_3, 4, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac2_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC2 source", rt5645_tdm_dac2_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac3_enum,
+ RT5645_TDM_CTRL_3, 0, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac3_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC3 source", rt5645_tdm_dac3_enum);
+
+/* MX-7a [14:12][10:8][6:4][2:0] */
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac0_enum,
+ RT5650_TDM_CTRL_4, 12, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac0_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC0 source", rt5650_tdm_dac0_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac1_enum,
+ RT5650_TDM_CTRL_4, 8, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac1_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC1 source", rt5650_tdm_dac1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac2_enum,
+ RT5650_TDM_CTRL_4, 4, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac2_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC2 source", rt5650_tdm_dac2_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac3_enum,
+ RT5650_TDM_CTRL_4, 0, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac3_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC3 source", rt5650_tdm_dac3_enum);
+
/* MX-2d [3] [2] */
static const char * const rt5650_a_dac1_src[] = {
"DAC1", "Stereo DAC Mixer"
@@ -1227,52 +1330,79 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
if (on) {
if (hp_amp_power_count <= 0) {
- /* depop parameters */
- snd_soc_update_bits(codec, RT5645_DEPOP_M2,
- RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
- regmap_write(rt5645->regmap, RT5645_PR_BASE +
- RT5645_HP_DCC_INT1, 0x9f01);
- mdelay(150);
- /* headphone amp power on */
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_FV1 | RT5645_PWR_FV2 , 0);
- snd_soc_update_bits(codec, RT5645_PWR_VOL,
- RT5645_PWR_HV_L | RT5645_PWR_HV_R,
- RT5645_PWR_HV_L | RT5645_PWR_HV_R);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_HP_L | RT5645_PWR_HP_R |
- RT5645_PWR_HA,
- RT5645_PWR_HP_L | RT5645_PWR_HP_R |
- RT5645_PWR_HA);
- mdelay(5);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_FV1 | RT5645_PWR_FV2,
- RT5645_PWR_FV1 | RT5645_PWR_FV2);
-
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
- RT5645_HP_CO_EN | RT5645_HP_SG_EN);
- regmap_write(rt5645->regmap, RT5645_PR_BASE +
- 0x14, 0x1aaa);
- regmap_write(rt5645->regmap, RT5645_PR_BASE +
- 0x24, 0x0430);
+ if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+ snd_soc_write(codec, RT5645_CHARGE_PUMP,
+ 0x0e06);
+ snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d);
+ 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);
+ } else {
+ /* depop parameters */
+ snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+ RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
+ snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_HP_DCC_INT1, 0x9f01);
+ mdelay(150);
+ /* headphone amp power on */
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2, 0);
+ snd_soc_update_bits(codec, RT5645_PWR_VOL,
+ RT5645_PWR_HV_L | RT5645_PWR_HV_R,
+ RT5645_PWR_HV_L | RT5645_PWR_HV_R);
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+ RT5645_PWR_HA,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+ RT5645_PWR_HA);
+ mdelay(5);
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2);
+
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
+ RT5645_HP_CO_EN | RT5645_HP_SG_EN);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x14, 0x1aaa);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x24, 0x0430);
+ }
}
hp_amp_power_count++;
} else {
hp_amp_power_count--;
if (hp_amp_power_count <= 0) {
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
- RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
- RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
- /* headphone amp power down */
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_HP_L | RT5645_PWR_HP_R |
- RT5645_PWR_HA, 0);
- snd_soc_update_bits(codec, RT5645_DEPOP_M2,
- RT5645_DEPOP_MASK, 0);
+ if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+ 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);
+ msleep(100);
+ snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001);
+
+ } else {
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_HP_SG_MASK |
+ RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK,
+ RT5645_HP_SG_DIS |
+ RT5645_HP_L_SMT_DIS |
+ RT5645_HP_R_SMT_DIS);
+ /* headphone amp power down */
+ snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000);
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+ RT5645_PWR_HA, 0);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+ RT5645_DEPOP_MASK, 0);
+ }
}
}
}
@@ -1287,56 +1417,52 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
hp_amp_power(codec, 1);
/* headphone unmute sequence */
- if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
- } else {
+ if (rt5645->codec_type == CODEC_TYPE_RT5645) {
snd_soc_update_bits(codec, RT5645_DEPOP_M3,
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
RT5645_CP_FQ3_MASK,
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_RSTN_MASK, RT5645_RSTN_EN);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
+ RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+ msleep(40);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
+ RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
}
- regmap_write(rt5645->regmap,
- RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_RSTN_MASK, RT5645_RSTN_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
- RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
- RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
- msleep(40);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
- RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
- RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
break;
case SND_SOC_DAPM_PRE_PMD:
/* headphone mute sequence */
- if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
- } else {
+ if (rt5645->codec_type == CODEC_TYPE_RT5645) {
snd_soc_update_bits(codec, RT5645_DEPOP_M3,
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
RT5645_CP_FQ3_MASK,
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_RSTP_MASK, RT5645_RSTP_EN);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
+ RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+ msleep(30);
}
- regmap_write(rt5645->regmap,
- RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_RSTP_MASK, RT5645_RSTP_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
- RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
- RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
- msleep(30);
hp_amp_power(codec, 0);
break;
@@ -1571,20 +1697,33 @@ 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("IF1 ADC Mux", SND_SOC_NOPM,
+ 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),
/* Digital Interface */
SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1,
RT5645_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
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 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("IF1 DAC2 R", 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),
@@ -1726,6 +1865,24 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
0, 0, &rt5650_a_dac2_l_mux),
SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM,
0, 0, &rt5650_a_dac2_r_mux),
+
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc1_in_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc2_in_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc3_in_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc_in_mux),
+
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac0_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac1_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac2_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac3_tdm_sel_mux),
};
static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
@@ -1848,42 +2005,32 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "IF_ADC2", NULL, "Mono ADC MIXR" },
{ "VAD_ADC", NULL, "VAD ADC Mux" },
- { "IF1 ADC Mux", "IF_ADC1", "IF_ADC1" },
- { "IF1 ADC Mux", "IF_ADC2", "IF_ADC2" },
- { "IF1 ADC Mux", "VAD_ADC", "VAD_ADC" },
-
{ "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" },
{ "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" },
{ "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" },
{ "IF1 ADC", NULL, "I2S1" },
- { "IF1 ADC", NULL, "IF1 ADC Mux" },
{ "IF2 ADC", NULL, "I2S2" },
{ "IF2 ADC", NULL, "IF2 ADC Mux" },
- { "AIF1TX", NULL, "IF1 ADC" },
- { "AIF1TX", NULL, "IF2 ADC" },
{ "AIF2TX", NULL, "IF2 ADC" },
+ { "IF1 DAC0", NULL, "AIF1RX" },
{ "IF1 DAC1", NULL, "AIF1RX" },
{ "IF1 DAC2", NULL, "AIF1RX" },
+ { "IF1 DAC3", NULL, "AIF1RX" },
{ "IF2 DAC", NULL, "AIF2RX" },
+ { "IF1 DAC0", NULL, "I2S1" },
{ "IF1 DAC1", NULL, "I2S1" },
{ "IF1 DAC2", NULL, "I2S1" },
+ { "IF1 DAC3", NULL, "I2S1" },
{ "IF2 DAC", NULL, "I2S2" },
- { "IF1 DAC2 L", NULL, "IF1 DAC2" },
- { "IF1 DAC2 R", NULL, "IF1 DAC2" },
- { "IF1 DAC1 L", NULL, "IF1 DAC1" },
- { "IF1 DAC1 R", NULL, "IF1 DAC1" },
{ "IF2 DAC L", NULL, "IF2 DAC" },
{ "IF2 DAC R", NULL, "IF2 DAC" },
- { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" },
{ "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" },
-
- { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" },
{ "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" },
{ "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
@@ -1893,14 +2040,12 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
{ "DAC1 MIXR", NULL, "dac stereo1 filter" },
- { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" },
{ "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" },
{ "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" },
{ "DAC L2 Mux", "VAD_ADC", "VAD_ADC" },
{ "DAC L2 Volume", NULL, "DAC L2 Mux" },
{ "DAC L2 Volume", NULL, "dac mono left filter" },
- { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" },
{ "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" },
{ "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" },
{ "DAC R2 Mux", "Haptic", "Haptic Generator" },
@@ -2038,6 +2183,80 @@ static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = {
{ "DAC R1", NULL, "A DAC1 R Mux" },
{ "DAC L2", NULL, "A DAC2 L Mux" },
{ "DAC R2", NULL, "A DAC2 R Mux" },
+
+ { "RT5650 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" },
+ { "RT5650 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" },
+ { "RT5650 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" },
+ { "RT5650 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" },
+
+ { "RT5650 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" },
+ { "RT5650 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" },
+ { "RT5650 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" },
+ { "RT5650 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" },
+
+ { "RT5650 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" },
+ { "RT5650 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" },
+ { "RT5650 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" },
+ { "RT5650 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" },
+
+ { "IF1 ADC", NULL, "RT5650 IF1 ADC1 Swap Mux" },
+ { "IF1 ADC", NULL, "RT5650 IF1 ADC2 Swap Mux" },
+ { "IF1 ADC", NULL, "RT5650 IF1 ADC3 Swap Mux" },
+
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/DAC_REF/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/Null/DAC_REF", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/IF_ADC2/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/Null/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/DAC_REF/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/IF_ADC2/DAC_REF", "IF1 ADC" },
+
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/DAC_REF/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/Null/DAC_REF", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/IF_ADC1/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/Null/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/DAC_REF/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/IF_ADC1/DAC_REF", "IF1 ADC" },
+
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/IF_ADC2/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/Null/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/IF_ADC1/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/Null/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC1/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC2/IF_ADC1", "IF1 ADC" },
+
+ { "RT5650 IF1 ADC Mux", "Null/IF_ADC1/IF_ADC2/DAC_REF", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "Null/IF_ADC1/DAC_REF/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "Null/IF_ADC2/IF_ADC1/DAC_REF", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "Null/IF_ADC2/DAC_REF/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC1/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC2/IF_ADC1", "IF1 ADC" },
+ { "AIF1TX", NULL, "RT5650 IF1 ADC Mux" },
+
+ { "RT5650 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" },
+ { "RT5650 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" },
+ { "RT5650 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" },
+ { "RT5650 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" },
+
+ { "RT5650 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" },
+ { "RT5650 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" },
+ { "RT5650 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" },
+ { "RT5650 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" },
+
+ { "RT5650 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" },
+ { "RT5650 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" },
+ { "RT5650 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" },
+ { "RT5650 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" },
+
+ { "RT5650 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" },
+ { "RT5650 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" },
+ { "RT5650 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" },
+ { "RT5650 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" },
+
+ { "DAC1 L Mux", "IF1 DAC", "RT5650 IF1 DAC1 L Mux" },
+ { "DAC1 R Mux", "IF1 DAC", "RT5650 IF1 DAC1 R Mux" },
+
+ { "DAC L2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 L Mux" },
+ { "DAC R2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 R Mux" },
};
static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
@@ -2045,6 +2264,57 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
{ "DAC R1", NULL, "Stereo DAC MIXR" },
{ "DAC L2", NULL, "Mono DAC MIXL" },
{ "DAC R2", NULL, "Mono DAC MIXR" },
+
+ { "RT5645 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" },
+ { "RT5645 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" },
+ { "RT5645 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" },
+ { "RT5645 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" },
+
+ { "RT5645 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" },
+ { "RT5645 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" },
+ { "RT5645 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" },
+ { "RT5645 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" },
+
+ { "RT5645 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" },
+ { "RT5645 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" },
+ { "RT5645 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" },
+ { "RT5645 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" },
+
+ { "IF1 ADC", NULL, "RT5645 IF1 ADC1 Swap Mux" },
+ { "IF1 ADC", NULL, "RT5645 IF1 ADC2 Swap Mux" },
+ { "IF1 ADC", NULL, "RT5645 IF1 ADC3 Swap Mux" },
+
+ { "RT5645 IF1 ADC Mux", "IF_ADC1/IF_ADC2/VAD_ADC", "IF1 ADC" },
+ { "RT5645 IF1 ADC Mux", "IF_ADC2/IF_ADC1/VAD_ADC", "IF1 ADC" },
+ { "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC1/IF_ADC2", "IF1 ADC" },
+ { "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC2/IF_ADC1", "IF1 ADC" },
+ { "AIF1TX", NULL, "RT5645 IF1 ADC Mux" },
+
+ { "RT5645 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" },
+ { "RT5645 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" },
+ { "RT5645 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" },
+ { "RT5645 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" },
+
+ { "RT5645 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" },
+ { "RT5645 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" },
+ { "RT5645 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" },
+ { "RT5645 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" },
+
+ { "RT5645 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" },
+ { "RT5645 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" },
+ { "RT5645 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" },
+ { "RT5645 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" },
+
+ { "RT5645 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" },
+ { "RT5645 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" },
+ { "RT5645 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" },
+ { "RT5645 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" },
+
+ { "DAC1 L Mux", "IF1 DAC", "RT5645 IF1 DAC1 L Mux" },
+ { "DAC1 R Mux", "IF1 DAC", "RT5645 IF1 DAC1 R Mux" },
+
+ { "DAC L2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 L Mux" },
+ { "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" },
};
static int rt5645_hw_params(struct snd_pcm_substream *substream,
@@ -2102,9 +2372,8 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
switch (dai->id) {
case RT5645_AIF1:
- mask_clk = RT5645_I2S_BCLK_MS1_MASK | RT5645_I2S_PD1_MASK;
- val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT |
- pre_div << RT5645_I2S_PD1_SFT;
+ mask_clk = RT5645_I2S_PD1_MASK;
+ val_clk = pre_div << RT5645_I2S_PD1_SFT;
snd_soc_update_bits(codec, RT5645_I2S1_SDP,
(0x3 << dl_sft), (val_len << dl_sft));
snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
@@ -2369,6 +2638,8 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int rt5645_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
@@ -2399,8 +2670,9 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
- snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
- RT5645_DIG_GATE_CTRL, 0);
+ if (!rt5645->en_button_func)
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ RT5645_DIG_GATE_CTRL, 0);
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2 |
@@ -2410,72 +2682,228 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int rt5645_jack_detect(struct snd_soc_codec *codec)
+static int rt5650_calibration(struct rt5645_priv *rt5645)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- int gpio_state, jack_type = 0;
- unsigned int val;
+ int val, i;
+ int ret = -1;
- if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
- dev_err(codec->dev, "invalid gpio\n");
- return -EINVAL;
+ 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);
}
- gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
+ 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;
+}
- dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
- gpio_state);
+static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
- (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) {
- snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2");
- 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);
+ 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_update_bits(codec,
+ RT5645_INT_IRQ_ST, 0x8, 0x8);
+ snd_soc_update_bits(codec,
+ RT5650_4BTN_IL_CMD2, 0x8000, 0x8000);
+ snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+ pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1,
+ snd_soc_read(codec, RT5650_4BTN_IL_CMD1));
+ } else {
+ 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);
+ }
+}
+
+static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
- snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
- snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
+ if (jack_insert) {
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006);
- snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
- RT5645_CBJ_MN_JD, 0);
- snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
- RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
+ 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 {
+ /* Power up necessary bits for JD if dapm is
+ not ready yet */
+ regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1,
+ RT5645_PWR_MB | RT5645_PWR_VREF2,
+ RT5645_PWR_MB | RT5645_PWR_VREF2);
+ regmap_update_bits(rt5645->regmap, RT5645_PWR_MIXER,
+ RT5645_PWR_LDO2, RT5645_PWR_LDO2);
+ regmap_update_bits(rt5645->regmap, RT5645_PWR_VOL,
+ RT5645_PWR_MIC_DET, RT5645_PWR_MIC_DET);
+ }
- msleep(400);
- val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
+ 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);
+ msleep(100);
+ regmap_update_bits(rt5645->regmap,
+ RT5645_IN1_CTRL2, 0x1000, 0x0000);
+
+ msleep(450);
+ regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val);
+ val &= 0x7;
dev_dbg(codec->dev, "val = %d\n", val);
- if (val == 1 || val == 2)
- jack_type = SND_JACK_HEADSET;
- else
- jack_type = SND_JACK_HEADPHONE;
+ if (val == 1 || val == 2) {
+ rt5645->jack_type = SND_JACK_HEADSET;
+ if (rt5645->en_button_func) {
+ 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);
+ rt5645->jack_type = SND_JACK_HEADPHONE;
+ }
- snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
- snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
- 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 { /* jack out */
+ rt5645->jack_type = 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);
+ }
+ }
}
- snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
- snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
- return 0;
+ return rt5645->jack_type;
}
+static int rt5645_irq_detection(struct rt5645_priv *rt5645);
+static irqreturn_t rt5645_irq(int irq, void *data);
+
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 *hp_jack, struct snd_soc_jack *mic_jack,
+ struct snd_soc_jack *btn_jack)
{
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
rt5645->hp_jack = hp_jack;
rt5645->mic_jack = mic_jack;
- rt5645_jack_detect(codec);
+ rt5645->btn_jack = btn_jack;
+ if (rt5645->btn_jack && rt5645->codec_type == CODEC_TYPE_RT5650) {
+ rt5645->en_button_func = true;
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
+ regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1,
+ RT5645_HP_CB_MASK, RT5645_HP_CB_PU);
+ regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1,
+ RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
+ }
+ rt5645_irq(0, rt5645);
return 0;
}
@@ -2486,7 +2914,7 @@ 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_jack_detect(rt5645->codec);
+ rt5645_irq_detection(rt5645);
}
static irqreturn_t rt5645_irq(int irq, void *data)
@@ -2499,6 +2927,120 @@ static irqreturn_t rt5645_irq(int irq, void *data)
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;
+
+ switch (rt5645->pdata.jd_mode) {
+ case 0: /* Not using rt5645 JD */
+ if (rt5645->gpiod_hp_det) {
+ gpio_state = gpiod_get_value(rt5645->gpiod_hp_det);
+ dev_dbg(rt5645->codec->dev, "gpio_state = %d\n",
+ gpio_state);
+ report = rt5645_jack_detect(rt5645->codec, gpio_state);
+ }
+ snd_soc_jack_report(rt5645->hp_jack,
+ report, SND_JACK_HEADPHONE);
+ snd_soc_jack_report(rt5645->mic_jack,
+ report, SND_JACK_MICROPHONE);
+ return report;
+ case 1: /* 2 port */
+ val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070;
+ break;
+ default: /* 1 port */
+ val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020;
+ break;
+
+ }
+
+ switch (val) {
+ /* jack in */
+ case 0x30: /* 2 port */
+ case 0x0: /* 1 port or 2 port */
+ if (rt5645->jack_type == 0) {
+ report = rt5645_jack_detect(rt5645->codec, 1);
+ /* for push button and jack out */
+ break;
+ }
+ btn_type = 0;
+ if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) {
+ /* button pressed */
+ report = SND_JACK_HEADSET;
+ btn_type = rt5645_button_detect(rt5645->codec);
+ /* rt5650 can report three kinds of button behavior,
+ one click, double click and hold. However,
+ currently we will report button pressed/released
+ event. So all the three button behaviors are
+ treated as button pressed. */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ report |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ report |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ report |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ report |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ dev_err(rt5645->codec->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ if (btn_type == 0)/* button release */
+ report = rt5645->jack_type;
+
+ break;
+ /* jack out */
+ case 0x70: /* 2 port */
+ case 0x10: /* 2 port */
+ case 0x20: /* 1 port */
+ report = 0;
+ snd_soc_update_bits(rt5645->codec,
+ RT5645_INT_IRQ_ST, 0x1, 0x0);
+ rt5645_jack_detect(rt5645->codec, 0);
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE);
+ snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE);
+ if (rt5645->en_button_func)
+ snd_soc_jack_report(rt5645->btn_jack,
+ report, SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ return report;
+}
+
static int rt5645_probe(struct snd_soc_codec *codec)
{
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
@@ -2521,12 +3063,10 @@ static int rt5645_probe(struct snd_soc_codec *codec)
break;
}
- rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
/* for JD function */
- if (rt5645->pdata.en_jd_func) {
+ 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);
@@ -2666,6 +3206,46 @@ static struct acpi_device_id rt5645_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
#endif
+static struct rt5645_platform_data *rt5645_pdata;
+
+static struct rt5645_platform_data strago_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC1_DISABLE,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+};
+
+static int strago_quirk_cb(const struct dmi_system_id *id)
+{
+ rt5645_pdata = &strago_platform_data;
+
+ return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_braswell[] = {
+ {
+ .ident = "Intel Strago",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
+ },
+ },
+ { }
+};
+
+static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
+{
+ rt5645->pdata.in2_diff = device_property_read_bool(dev,
+ "realtek,in2-differential");
+ device_property_read_u32(dev,
+ "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin);
+ device_property_read_u32(dev,
+ "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin);
+ device_property_read_u32(dev,
+ "realtek,jd-mode", &rt5645->pdata.jd_mode);
+
+ return 0;
+}
+
static int rt5645_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -2684,6 +3264,18 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5645->pdata = *pdata;
+ else if (dmi_check_system(dmi_platform_intel_braswell))
+ rt5645->pdata = *rt5645_pdata;
+ else
+ rt5645_parse_dt(rt5645, &i2c->dev);
+
+ rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
+ GPIOD_IN);
+
+ if (IS_ERR(rt5645->gpiod_hp_det)) {
+ dev_err(&i2c->dev, "failed to initialize gpiod\n");
+ return PTR_ERR(rt5645->gpiod_hp_det);
+ }
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
if (IS_ERR(rt5645->regmap)) {
@@ -2709,6 +3301,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
+ if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+ ret = rt5650_calibration(rt5645);
+
+ if (ret < 0)
+ pr_err("calibration failed!\n");
+ }
+
regmap_write(rt5645->regmap, RT5645_RESET, 0);
ret = regmap_register_patch(rt5645->regmap, init_list,
@@ -2728,84 +3327,76 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
RT5645_IN_DF2, RT5645_IN_DF2);
- if (rt5645->pdata.dmic_en) {
+ if (rt5645->pdata.dmic1_data_pin || rt5645->pdata.dmic2_data_pin) {
regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL);
+ }
+ switch (rt5645->pdata.dmic1_data_pin) {
+ case RT5645_DMIC_DATA_IN2N:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N);
+ break;
- switch (rt5645->pdata.dmic1_data_pin) {
- case RT5645_DMIC_DATA_IN2N:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N);
- break;
-
- case RT5645_DMIC_DATA_GPIO5:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5);
- regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
- RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA);
- break;
-
- case RT5645_DMIC_DATA_GPIO11:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11);
- regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
- RT5645_GP11_PIN_MASK,
- RT5645_GP11_PIN_DMIC1_SDA);
- break;
+ case RT5645_DMIC_DATA_GPIO5:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA);
+ break;
- default:
- break;
- }
+ case RT5645_DMIC_DATA_GPIO11:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP11_PIN_MASK,
+ RT5645_GP11_PIN_DMIC1_SDA);
+ break;
- switch (rt5645->pdata.dmic2_data_pin) {
- case RT5645_DMIC_DATA_IN2P:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P);
- break;
+ default:
+ break;
+ }
- case RT5645_DMIC_DATA_GPIO6:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6);
- regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
- RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA);
- break;
+ switch (rt5645->pdata.dmic2_data_pin) {
+ case RT5645_DMIC_DATA_IN2P:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P);
+ break;
- case RT5645_DMIC_DATA_GPIO10:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10);
- regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
- RT5645_GP10_PIN_MASK,
- RT5645_GP10_PIN_DMIC2_SDA);
- break;
+ case RT5645_DMIC_DATA_GPIO6:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA);
+ break;
- case RT5645_DMIC_DATA_GPIO12:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12);
- regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
- RT5645_GP12_PIN_MASK,
- RT5645_GP12_PIN_DMIC2_SDA);
- break;
+ case RT5645_DMIC_DATA_GPIO10:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP10_PIN_MASK,
+ RT5645_GP10_PIN_DMIC2_SDA);
+ break;
- default:
- break;
- }
+ case RT5645_DMIC_DATA_GPIO12:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP12_PIN_MASK,
+ RT5645_GP12_PIN_DMIC2_SDA);
+ break;
+ default:
+ break;
}
- if (rt5645->pdata.en_jd_func) {
+ if (rt5645->pdata.jd_mode) {
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
- RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
- RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
+ 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_JD_CTRL3,
- RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL,
- RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL);
+ RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
- RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
- }
-
- if (rt5645->pdata.jd_mode) {
+ RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN);
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
@@ -2837,6 +3428,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
}
+ INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+
if (rt5645->i2c->irq) {
ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@@ -2845,18 +3438,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
}
- if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
- ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645");
- if (ret)
- dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n");
-
- ret = gpio_direction_input(rt5645->pdata.hp_det_gpio);
- if (ret)
- dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
- }
-
- INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
-
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
rt5645_dai, ARRAY_SIZE(rt5645_dai));
}
@@ -2870,9 +3451,6 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
cancel_delayed_work_sync(&rt5645->jack_detect_work);
- if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
- gpio_free(rt5645->pdata.hp_det_gpio);
-
snd_soc_unregister_codec(&i2c->dev);
return 0;
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index db78e9462876..0353a6a273ab 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -105,6 +105,7 @@
#define RT5645_TDM_CTRL_1 0x77
#define RT5645_TDM_CTRL_2 0x78
#define RT5645_TDM_CTRL_3 0x79
+#define RT5650_TDM_CTRL_4 0x7a
/* Function - Analog */
#define RT5645_GLB_CLK 0x80
@@ -942,10 +943,6 @@
#define RT5645_I2S2_SDI_I2S2 (0x1 << 6)
/* ADC/DAC Clock Control 1 (0x73) */
-#define RT5645_I2S_BCLK_MS1_MASK (0x1 << 15)
-#define RT5645_I2S_BCLK_MS1_SFT 15
-#define RT5645_I2S_BCLK_MS1_32 (0x0 << 15)
-#define RT5645_I2S_BCLK_MS1_64 (0x1 << 15)
#define RT5645_I2S_PD1_MASK (0x7 << 12)
#define RT5645_I2S_PD1_SFT 12
#define RT5645_I2S_PD1_1 (0x0 << 12)
@@ -1067,13 +1064,14 @@
#define RT5645_SCLK_SRC_SFT 14
#define RT5645_SCLK_SRC_MCLK (0x0 << 14)
#define RT5645_SCLK_SRC_PLL1 (0x1 << 14)
-#define RT5645_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */
-#define RT5645_PLL1_SRC_MASK (0x3 << 12)
-#define RT5645_PLL1_SRC_SFT 12
-#define RT5645_PLL1_SRC_MCLK (0x0 << 12)
-#define RT5645_PLL1_SRC_BCLK1 (0x1 << 12)
-#define RT5645_PLL1_SRC_BCLK2 (0x2 << 12)
-#define RT5645_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5645_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5645_PLL1_SRC_MASK (0x7 << 11)
+#define RT5645_PLL1_SRC_SFT 11
+#define RT5645_PLL1_SRC_MCLK (0x0 << 11)
+#define RT5645_PLL1_SRC_BCLK1 (0x1 << 11)
+#define RT5645_PLL1_SRC_BCLK2 (0x2 << 11)
+#define RT5645_PLL1_SRC_BCLK3 (0x3 << 11)
+#define RT5645_PLL1_SRC_RCCLK (0x4 << 11)
#define RT5645_PLL1_PD_MASK (0x1 << 3)
#define RT5645_PLL1_PD_SFT 3
#define RT5645_PLL1_PD_1 (0x0 << 3)
@@ -2147,6 +2145,7 @@ enum {
};
enum {
+ RT5645_DMIC1_DISABLE,
RT5645_DMIC_DATA_IN2P,
RT5645_DMIC_DATA_GPIO6,
RT5645_DMIC_DATA_GPIO10,
@@ -2154,6 +2153,7 @@ enum {
};
enum {
+ RT5645_DMIC2_DISABLE,
RT5645_DMIC_DATA_IN2N,
RT5645_DMIC_DATA_GPIO5,
RT5645_DMIC_DATA_GPIO11,
@@ -2182,8 +2182,10 @@ struct rt5645_priv {
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;
@@ -2196,9 +2198,12 @@ struct rt5645_priv {
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 *hp_jack, struct snd_soc_jack *mic_jack,
+ struct snd_soc_jack *btn_jack);
#endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 9f4c7be6d798..a3506e193abc 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1571,7 +1571,7 @@ static int rt5651_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, RT5651_PWR_ANLG1,
RT5651_PWR_VREF1 | RT5651_PWR_MB |
RT5651_PWR_BG | RT5651_PWR_VREF2,
@@ -1604,7 +1604,6 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1625,7 +1624,7 @@ static int rt5651_probe(struct snd_soc_codec *codec)
RT5651_PWR_FV1 | RT5651_PWR_FV2,
RT5651_PWR_FV1 | RT5651_PWR_FV2);
- rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index cc7f84a150a7..a9123d414178 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -51,12 +51,11 @@ static const struct regmap_range_cfg rt5670_ranges[] = {
.window_len = 0x1, },
};
-static struct reg_default init_list[] = {
+static const struct reg_default init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
{ RT5670_PR_BASE + 0x38, 0x3ba1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
};
-#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5670_reg[] = {
{ 0x00, 0x0000 },
@@ -416,12 +415,12 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
{
int val;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
if (jack_insert) {
- snd_soc_dapm_force_enable_pin(&codec->dapm,
- "Mic Det Power");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_sync(dapm);
snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
@@ -447,15 +446,15 @@ static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
} else {
snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = SND_JACK_HEADPHONE;
- snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_sync(dapm);
}
} else {
snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = 0;
- snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_sync(dapm);
}
return rt5670->jack_type;
@@ -2603,7 +2602,7 @@ static int rt5670_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, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2,
@@ -2647,30 +2646,30 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
static int rt5670_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
case RT5670_ID_5670:
case RT5670_ID_5671:
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5670_specific_dapm_widgets,
ARRAY_SIZE(rt5670_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5670_specific_dapm_routes,
ARRAY_SIZE(rt5670_specific_dapm_routes));
break;
case RT5670_ID_5672:
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5672_specific_dapm_widgets,
ARRAY_SIZE(rt5672_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5672_specific_dapm_routes,
ARRAY_SIZE(rt5672_specific_dapm_routes));
break;
@@ -2809,7 +2808,7 @@ static const struct i2c_device_id rt5670_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
-static struct acpi_device_id rt5670_acpi_match[] = {
+static const struct acpi_device_id rt5670_acpi_match[] = {
{ "10EC5670", 0},
{ },
};
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 169aa471ffbd..31d969ac1192 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -820,7 +820,7 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
return 0;
@@ -1060,6 +1060,7 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
unsigned int asrc5_mask = 0, asrc5_value = 0;
unsigned int asrc6_mask = 0, asrc6_value = 0;
unsigned int asrc7_mask = 0, asrc7_value = 0;
+ unsigned int asrc8_mask = 0, asrc8_value = 0;
switch (clk_src) {
case RT5677_CLK_SEL_SYS:
@@ -1196,10 +1197,108 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask,
asrc7_value);
+ /* ASRC 8 */
+ if (filter_mask & RT5677_I2S1_SOURCE) {
+ asrc8_mask |= RT5677_I2S1_CLK_SEL_MASK;
+ asrc8_value = (asrc8_value & ~RT5677_I2S1_CLK_SEL_MASK)
+ | ((clk_src - 1) << RT5677_I2S1_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5677_I2S2_SOURCE) {
+ asrc8_mask |= RT5677_I2S2_CLK_SEL_MASK;
+ asrc8_value = (asrc8_value & ~RT5677_I2S2_CLK_SEL_MASK)
+ | ((clk_src - 1) << RT5677_I2S2_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5677_I2S3_SOURCE) {
+ asrc8_mask |= RT5677_I2S3_CLK_SEL_MASK;
+ asrc8_value = (asrc8_value & ~RT5677_I2S3_CLK_SEL_MASK)
+ | ((clk_src - 1) << RT5677_I2S3_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5677_I2S4_SOURCE) {
+ asrc8_mask |= RT5677_I2S4_CLK_SEL_MASK;
+ asrc8_value = (asrc8_value & ~RT5677_I2S4_CLK_SEL_MASK)
+ | ((clk_src - 1) << RT5677_I2S4_CLK_SEL_SFT);
+ }
+
+ if (asrc8_mask)
+ regmap_update_bits(rt5677->regmap, RT5677_ASRC_8, asrc8_mask,
+ asrc8_value);
+
return 0;
}
EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src);
+static int rt5677_dmic_use_asrc(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 rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int asrc_setting;
+
+ switch (source->shift) {
+ case 11:
+ regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+ asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >>
+ RT5677_AD_STO1_CLK_SEL_SFT;
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+ break;
+
+ case 10:
+ regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+ asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >>
+ RT5677_AD_STO2_CLK_SEL_SFT;
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+ break;
+
+ case 9:
+ regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+ asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >>
+ RT5677_AD_STO3_CLK_SEL_SFT;
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+ break;
+
+ case 8:
+ regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+ asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >>
+ RT5677_AD_STO4_CLK_SEL_SFT;
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+ break;
+
+ case 7:
+ regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
+ asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >>
+ RT5677_AD_MONOL_CLK_SEL_SFT;
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+ break;
+
+ case 6:
+ regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
+ asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >>
+ RT5677_AD_MONOR_CLK_SEL_SFT;
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
/* Digital Mixer */
static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER,
@@ -2479,7 +2578,7 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- if (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+ if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON &&
!rt5677->is_vref_slow) {
mdelay(20);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -3057,12 +3156,12 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
- { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
- { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
- { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", can_use_asrc },
- { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", can_use_asrc },
- { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc },
- { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc },
+ { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", rt5677_dmic_use_asrc },
+ { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", rt5677_dmic_use_asrc },
+ { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", rt5677_dmic_use_asrc },
+ { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", rt5677_dmic_use_asrc },
+ { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", rt5677_dmic_use_asrc },
+ { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", rt5677_dmic_use_asrc },
{ "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
{ "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
{ "I2S3", NULL, "I2S3 ASRC", can_use_asrc},
@@ -4353,7 +4452,7 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
rt5677_set_dsp_vad(codec, false);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -4395,7 +4494,6 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -4606,22 +4704,23 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
static int rt5677_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
int i;
rt5677->codec = codec;
if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5677_dmic2_clk_2,
ARRAY_SIZE(rt5677_dmic2_clk_2));
} else { /*use dmic1 clock by default*/
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5677_dmic2_clk_1,
ARRAY_SIZE(rt5677_dmic2_clk_1));
}
- rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
@@ -4667,6 +4766,8 @@ static int rt5677_remove(struct snd_soc_codec *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);
return 0;
}
@@ -4682,6 +4783,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
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);
}
return 0;
@@ -4692,10 +4795,13 @@ 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)) {
+ 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))
msleep(10);
- }
regcache_cache_only(rt5677->regmap, false);
regcache_sync(rt5677->regmap);
@@ -4933,6 +5039,8 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
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).
@@ -4943,6 +5051,9 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
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);
@@ -5044,6 +5155,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
}
} else {
rt5677->pow_ldo2 = -EINVAL;
+ rt5677->reset_pin = -EINVAL;
}
if (gpio_is_valid(rt5677->pow_ldo2)) {
@@ -5055,6 +5167,21 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
rt5677->pow_ldo2, 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;
+ }
+ }
+
+ if (gpio_is_valid(rt5677->pow_ldo2) ||
+ gpio_is_valid(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.
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 9dceb41d18ea..7eca38a23255 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1446,6 +1446,16 @@
#define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8)
#define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8
+/* ASRC Control 8 (0x8a) */
+#define RT5677_I2S1_CLK_SEL_MASK (0xf << 12)
+#define RT5677_I2S1_CLK_SEL_SFT 12
+#define RT5677_I2S2_CLK_SEL_MASK (0xf << 8)
+#define RT5677_I2S2_CLK_SEL_SFT 8
+#define RT5677_I2S3_CLK_SEL_MASK (0xf << 4)
+#define RT5677_I2S3_CLK_SEL_SFT 4
+#define RT5677_I2S4_CLK_SEL_MASK (0xf)
+#define RT5677_I2S4_CLK_SEL_SFT 0
+
/* VAD Function Control 4 (0x9f) */
#define RT5677_VAD_SRC_MASK (0x7 << 8)
#define RT5677_VAD_SRC_SFT 8
@@ -1744,6 +1754,10 @@ enum {
RT5677_AD_MONO_R_FILTER = (0x1 << 12),
RT5677_DSP_OB_0_3_FILTER = (0x1 << 13),
RT5677_DSP_OB_4_7_FILTER = (0x1 << 14),
+ RT5677_I2S1_SOURCE = (0x1 << 15),
+ RT5677_I2S2_SOURCE = (0x1 << 16),
+ RT5677_I2S3_SOURCE = (0x1 << 17),
+ RT5677_I2S4_SOURCE = (0x1 << 18),
};
struct rt5677_priv {
@@ -1762,6 +1776,7 @@ struct rt5677_priv {
int pll_in;
int pll_out;
int pow_ldo2; /* POW_LDO2 pin */
+ int 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 3593a1496056..e673f6ceb521 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -948,7 +948,7 @@ static int sgtl5000_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) {
ret = regulator_bulk_enable(
ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
@@ -979,7 +979,6 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1092,6 +1091,19 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
}
/*
+ * This precalculated table contains all (vag_val * 100 / lo_calcntrl) results
+ * to select an appropriate lo_vol_* in SGTL5000_CHIP_LINE_OUT_VOL
+ * The calculatation was done for all possible register values which
+ * is the array index and the following formula: 10^((idx−15)/40) * 100
+ */
+static const u8 vol_quot_table[] = {
+ 42, 45, 47, 50, 53, 56, 60, 63,
+ 67, 71, 75, 79, 84, 89, 94, 100,
+ 106, 112, 119, 126, 133, 141, 150, 158,
+ 168, 178, 188, 200, 211, 224, 237, 251
+};
+
+/*
* sgtl5000 has 3 internal power supplies:
* 1. VAG, normally set to vdda/2
* 2. charge pump, set to different value
@@ -1111,6 +1123,10 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
u16 ana_pwr;
u16 lreg_ctrl;
int vag;
+ int lo_vag;
+ int vol_quot;
+ int lo_vol;
+ size_t i;
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
@@ -1198,23 +1214,45 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT);
/* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
- vag = vddio / 2;
- if (vag <= SGTL5000_LINE_OUT_GND_BASE)
- vag = 0;
- else if (vag >= SGTL5000_LINE_OUT_GND_BASE +
+ lo_vag = vddio / 2;
+ if (lo_vag <= SGTL5000_LINE_OUT_GND_BASE)
+ lo_vag = 0;
+ else if (lo_vag >= SGTL5000_LINE_OUT_GND_BASE +
SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX)
- vag = SGTL5000_LINE_OUT_GND_MAX;
+ lo_vag = SGTL5000_LINE_OUT_GND_MAX;
else
- vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+ lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) /
SGTL5000_LINE_OUT_GND_STP;
snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
SGTL5000_LINE_OUT_CURRENT_MASK |
SGTL5000_LINE_OUT_GND_MASK,
- vag << SGTL5000_LINE_OUT_GND_SHIFT |
+ lo_vag << SGTL5000_LINE_OUT_GND_SHIFT |
SGTL5000_LINE_OUT_CURRENT_360u <<
SGTL5000_LINE_OUT_CURRENT_SHIFT);
+ /*
+ * Set lineout output level in range (0..31)
+ * the same value is used for right and left channel
+ *
+ * Searching for a suitable index solving this formula:
+ * idx = 40 * log10(vag_val / lo_cagcntrl) + 15
+ */
+ vol_quot = (vag * 100) / lo_vag;
+ lo_vol = 0;
+ for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) {
+ if (vol_quot >= vol_quot_table[i])
+ lo_vol = i;
+ else
+ break;
+ }
+
+ snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL,
+ SGTL5000_LINE_OUT_VOL_RIGHT_MASK |
+ SGTL5000_LINE_OUT_VOL_LEFT_MASK,
+ lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT |
+ lo_vol << SGTL5000_LINE_OUT_VOL_LEFT_SHIFT);
+
return 0;
}
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
index 0a8e43c98a07..29cb44256044 100644
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ b/sound/soc/codecs/sirf-audio-codec.c
@@ -395,7 +395,7 @@ struct snd_soc_dai_driver sirf_audio_codec_dai = {
static int sirf_audio_codec_probe(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
pm_runtime_enable(codec->dev);
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index 7947c0ebb1ed..3a7de0159f24 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -194,7 +194,7 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
pr_debug("vaud_bias powering up pll\n");
/* power up the pll */
snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
@@ -205,17 +205,22 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
+ case SND_SOC_BIAS_OFF:
pr_debug("vaud_bias power up rail\n");
/* power up the rail */
snd_soc_write(codec, SN95031_VAUD,
BIT(2)|BIT(1)|BIT(0));
msleep(1);
- } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ break;
+ case SND_SOC_BIAS_PREPARE:
/* turn off pcm */
pr_debug("vaud_bias power dn pcm\n");
snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+ break;
+ default:
+ break;
}
break;
@@ -226,7 +231,6 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 67ea55adb307..f30de7639bb9 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -510,7 +510,7 @@ static int ssm2518_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)
ret = ssm2518_set_power(ssm2518, true);
break;
case SND_SOC_BIAS_OFF:
@@ -518,12 +518,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (ret)
- return ret;
-
- codec->dapm.bias_level = level;
-
- return 0;
+ return ret;
}
static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 314eaece1b7d..69a773aeb13d 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -473,7 +473,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -524,8 +523,8 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
static int ssm2602_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
@@ -549,7 +548,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
static int ssm2604_codec_probe(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index a984485108cd..938d2cb6d78b 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -353,7 +353,7 @@ static int ssm4567_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)
ret = ssm4567_set_power(ssm4567, true);
break;
case SND_SOC_BIAS_OFF:
@@ -361,12 +361,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (ret)
- return ret;
-
- codec->dapm.bias_level = level;
-
- return 0;
+ return ret;
}
static const struct snd_soc_dai_ops ssm4567_dai_ops = {
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 007a0e3bc273..60eff36260cb 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -819,7 +819,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
@@ -854,7 +854,6 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
sta32x->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -970,7 +969,7 @@ static int sta32x_probe(struct snd_soc_codec *codec)
if (sta32x->pdata->needs_esd_watchdog)
INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog);
- sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
@@ -1096,16 +1095,10 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
#endif
/* GPIOs */
- sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset");
- if (IS_ERR(sta32x->gpiod_nreset)) {
- ret = PTR_ERR(sta32x->gpiod_nreset);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- sta32x->gpiod_nreset = NULL;
- } else {
- gpiod_direction_output(sta32x->gpiod_nreset, 0);
- }
+ sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sta32x->gpiod_nreset))
+ return PTR_ERR(sta32x->gpiod_nreset);
/* regulators */
for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 669e3228241e..bd819a3f205a 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -853,7 +853,7 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(
ARRAY_SIZE(sta350->supplies),
sta350->supplies);
@@ -890,7 +890,6 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
sta350->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1037,7 +1036,7 @@ static int sta350_probe(struct snd_soc_codec *codec)
sta350->coef_shadow[60] = 0x400000;
sta350->coef_shadow[61] = 0x400000;
- sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
@@ -1218,8 +1217,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(sta350->gpiod_nreset))
return PTR_ERR(sta350->gpiod_nreset);
- sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down",
- GPIOD_OUT_LOW);
+ sta350->gpiod_power_down = devm_gpiod_get_optional(dev, "power-down",
+ GPIOD_OUT_LOW);
if (IS_ERR(sta350->gpiod_power_down))
return PTR_ERR(sta350->gpiod_power_down);
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index b0f436d10125..4f70378b2cfb 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -165,7 +165,7 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
FFX_CLK_ENB);
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)
regcache_sync(sta529->regmap);
snd_soc_update_bits(codec, STA529_FFXCFG0,
POWER_CNTLMSAK, POWER_STDBY);
@@ -179,12 +179,6 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
break;
}
- /*
- * store the label for powers down audio subsystem for suspend.This is
- * used by soc core layer
- */
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 6464caf72b21..ed4cca7f6779 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -236,7 +236,6 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -321,7 +320,7 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
.channels_max = 2,
.rates = SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
},
/* alsa ops */
.ops = &stac9766_dai_ops_digital,
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index dfb4ff5cc9ea..4f25a7d0efa2 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -34,6 +34,7 @@
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <sound/tas2552-plat.h>
+#include <dt-bindings/sound/tas2552.h>
#include "tas2552.h"
@@ -44,8 +45,8 @@ static struct reg_default tas2552_reg_defs[] = {
{TAS2552_OUTPUT_DATA, 0xc0},
{TAS2552_PDM_CFG, 0x01},
{TAS2552_PGA_GAIN, 0x00},
- {TAS2552_BOOST_PT_CTRL, 0x0f},
- {TAS2552_RESERVED_0D, 0x00},
+ {TAS2552_BOOST_APT_CTRL, 0x0f},
+ {TAS2552_RESERVED_0D, 0xbe},
{TAS2552_LIMIT_RATE_HYS, 0x08},
{TAS2552_CFG_2, 0xef},
{TAS2552_SER_CTRL_1, 0x00},
@@ -75,20 +76,47 @@ struct tas2552_data {
struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
struct gpio_desc *enable_gpio;
unsigned char regs[TAS2552_VBAT_DATA];
- unsigned int mclk;
-};
+ unsigned int pll_clkin;
+ int pll_clk_id;
+ unsigned int pdm_clk;
+ int pdm_clk_id;
-/* Input mux controls */
-static const char *tas2552_input_texts[] = {
- "Digital", "Analog"
+ unsigned int dai_fmt;
+ unsigned int tdm_delay;
};
+static int tas2552_post_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, TAS2552_RESERVED_0D, 0xc0);
+ snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5),
+ (1 << 5));
+ snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0);
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS,
+ TAS2552_SWS);
+ snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1);
+ snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
+ snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe);
+ break;
+ }
+ return 0;
+}
+
+/* Input mux controls */
+static const char * const tas2552_input_texts[] = {
+ "Digital", "Analog" };
static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
tas2552_input_texts);
-static const struct snd_kcontrol_new tas2552_input_mux_control[] = {
- SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum)
-};
+static const struct snd_kcontrol_new tas2552_input_mux_control =
+ SOC_DAPM_ENUM("Route", tas2552_input_mux_enum);
static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
{
@@ -96,12 +124,13 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
/* MUX Controls */
SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
- tas2552_input_mux_control),
+ &tas2552_input_mux_control),
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
SND_SOC_DAPM_OUTPUT("OUT")
};
@@ -116,125 +145,253 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
};
#ifdef CONFIG_PM
-static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
+static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
{
- u8 cfg1_reg;
+ u8 cfg1_reg = 0;
+
+ if (!tas2552->codec)
+ return;
if (sw_shutdown)
- cfg1_reg = 0;
- else
- cfg1_reg = TAS2552_SWS_MASK;
+ cfg1_reg = TAS2552_SWS;
- snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
- TAS2552_SWS_MASK, cfg1_reg);
+ snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS,
+ cfg1_reg);
}
#endif
-static int tas2552_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int tas2552_setup_pll(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
{
- struct snd_soc_codec *codec = dai->codec;
struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
- int sample_rate, pll_clk;
- int d;
- u8 p, j;
+ bool bypass_pll = false;
+ unsigned int pll_clk = params_rate(params) * 512;
+ unsigned int pll_clkin = tas2552->pll_clkin;
+ u8 pll_enable;
- if (!tas2552->mclk)
- return -EINVAL;
+ if (!pll_clkin) {
+ if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
+ return -EINVAL;
+
+ pll_clkin = snd_soc_params_to_bclk(params);
+ pll_clkin += tas2552->tdm_delay;
+ }
+ pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
- if (tas2552->mclk == TAS2552_245MHZ_CLK ||
- tas2552->mclk == TAS2552_225MHZ_CLK) {
+ if (pll_clkin == pll_clk)
+ bypass_pll = true;
+
+ if (bypass_pll) {
/* By pass the PLL configuration */
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
- TAS2552_PLL_BYPASS_MASK,
- TAS2552_PLL_BYPASS);
+ TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
} else {
/* Fill in the PLL control registers for J & D
- * PLL_CLK = (.5 * freq * J.D) / 2^p
+ * pll_clk = (.5 * pll_clkin * J.D) / 2^p
* Need to fill in J and D here based on incoming freq
*/
- p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
+ unsigned int d;
+ u8 j;
+ u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
+ u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
+
p = (p >> 7);
- sample_rate = params_rate(params);
-
- if (sample_rate == 48000)
- pll_clk = TAS2552_245MHZ_CLK;
- else if (sample_rate == 44100)
- pll_clk = TAS2552_225MHZ_CLK;
- else {
- dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
- params_rate(params));
- return -EINVAL;
+
+recalc:
+ j = (pll_clk * 2 * (1 << p)) / pll_clkin;
+ d = (pll_clk * 2 * (1 << p)) % pll_clkin;
+ d /= (pll_clkin / 10000);
+
+ if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
+ if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
+ pll_clkin = 1800000;
+ pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
+ TAS2552_PLL_SRC_MASK;
+ } else {
+ pll_clkin = snd_soc_params_to_bclk(params);
+ pll_clkin += tas2552->tdm_delay;
+ pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
+ TAS2552_PLL_SRC_MASK;
+ }
+ goto recalc;
}
- j = (pll_clk * 2 * (1 << p)) / tas2552->mclk;
- d = (pll_clk * 2 * (1 << p)) % tas2552->mclk;
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
+ pll_sel);
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
- TAS2552_PLL_J_MASK, j);
+ TAS2552_PLL_J_MASK, j);
+ /* Will clear the PLL_BYPASS bit */
snd_soc_write(codec, TAS2552_PLL_CTRL_2,
- (d >> 7) & TAS2552_PLL_D_UPPER_MASK);
+ TAS2552_PLL_D_UPPER(d));
snd_soc_write(codec, TAS2552_PLL_CTRL_3,
- d & TAS2552_PLL_D_LOWER_MASK);
+ TAS2552_PLL_D_LOWER(d));
+ }
+
+ /* Restore PLL status */
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
+ pll_enable);
+ return 0;
+}
+
+static int tas2552_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 tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ int cpf;
+ u8 ser_ctrl1_reg, wclk_rate;
+
+ switch (params_width(params)) {
+ case 16:
+ ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT;
+ cpf = 32 + tas2552->tdm_delay;
+ break;
+ case 20:
+ ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT;
+ cpf = 64 + tas2552->tdm_delay;
+ break;
+ case 24:
+ ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT;
+ cpf = 64 + tas2552->tdm_delay;
+ break;
+ case 32:
+ ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT;
+ cpf = 64 + tas2552->tdm_delay;
+ break;
+ default:
+ dev_err(codec->dev, "Not supported sample size: %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ if (cpf <= 32)
+ ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32;
+ else if (cpf <= 64)
+ ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64;
+ else if (cpf <= 128)
+ ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128;
+ else
+ ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
+
+ snd_soc_update_bits(codec, TAS2552_SER_CTRL_1,
+ TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
+ ser_ctrl1_reg);
+
+ switch (params_rate(params)) {
+ case 8000:
+ wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
+ break;
+ case 11025:
+ case 12000:
+ wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
+ break;
+ case 16000:
+ wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
+ break;
+ case 22050:
+ case 24000:
+ wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
+ break;
+ case 32000:
+ wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
+ break;
+ case 44100:
+ case 48000:
+ wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
+ break;
+ case 88200:
+ case 96000:
+ wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
+ break;
+ case 176400:
+ case 192000:
+ wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
+ break;
+ default:
+ dev_err(codec->dev, "Not supported sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
}
+ snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
+ wclk_rate);
+
+ return tas2552_setup_pll(codec, params);
+}
+
+#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
+ TAS2552_WCLKDIR | \
+ TAS2552_DATAFORMAT_MASK)
+static int tas2552_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ int delay = 0;
+
+ /* TDM slot selection only valid in DSP_A/_B mode */
+ if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+ delay += (tas2552->tdm_delay + 1);
+ else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+ delay += tas2552->tdm_delay;
+
+ /* Configure data delay */
+ snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
+
return 0;
}
static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
+ struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
u8 serial_format;
- u8 serial_control_mask;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
serial_format = 0x00;
break;
case SND_SOC_DAIFMT_CBS_CFM:
- serial_format = TAS2552_WORD_CLK_MASK;
+ serial_format = TAS2552_WCLKDIR;
break;
case SND_SOC_DAIFMT_CBM_CFS:
- serial_format = TAS2552_BIT_CLK_MASK;
+ serial_format = TAS2552_BCLKDIR;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK);
+ serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
break;
default:
dev_vdbg(codec->dev, "DAI Format master is not found\n");
return -EINVAL;
}
- serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK;
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- serial_format &= TAS2552_DAIFMT_I2S_MASK;
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_INV_MASK)) {
+ case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
break;
- case SND_SOC_DAIFMT_DSP_A:
- serial_format |= TAS2552_DAIFMT_DSP;
+ case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+ case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
+ serial_format |= TAS2552_DATAFORMAT_DSP;
break;
- case SND_SOC_DAIFMT_RIGHT_J:
- serial_format |= TAS2552_DAIFMT_RIGHT_J;
+ case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
+ serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
break;
- case SND_SOC_DAIFMT_LEFT_J:
- serial_format |= TAS2552_DAIFMT_LEFT_J;
+ case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
+ serial_format |= TAS2552_DATAFORMAT_LEFT_J;
break;
default:
dev_vdbg(codec->dev, "DAI Format is not found\n");
return -EINVAL;
}
+ tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- if (fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- serial_control_mask |= TAS2552_DATA_FORMAT_MASK;
-
- snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask,
- serial_format);
-
+ snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
+ serial_format);
return 0;
}
@@ -243,23 +400,85 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
{
struct snd_soc_codec *codec = dai->codec;
struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ u8 reg, mask, val;
+
+ switch (clk_id) {
+ case TAS2552_PLL_CLKIN_MCLK:
+ case TAS2552_PLL_CLKIN_IVCLKIN:
+ if (freq < 512000 || freq > 24576000) {
+ /* out of range PLL_CLKIN, fall back to use BCLK */
+ dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n",
+ freq);
+ clk_id = TAS2552_PLL_CLKIN_BCLK;
+ freq = 0;
+ }
+ /* fall through */
+ case TAS2552_PLL_CLKIN_BCLK:
+ case TAS2552_PLL_CLKIN_1_8_FIXED:
+ mask = TAS2552_PLL_SRC_MASK;
+ val = (clk_id << 3) & mask; /* bit 4:5 in the register */
+ reg = TAS2552_CFG_1;
+ tas2552->pll_clk_id = clk_id;
+ tas2552->pll_clkin = freq;
+ break;
+ case TAS2552_PDM_CLK_PLL:
+ case TAS2552_PDM_CLK_IVCLKIN:
+ case TAS2552_PDM_CLK_BCLK:
+ case TAS2552_PDM_CLK_MCLK:
+ mask = TAS2552_PDM_CLK_SEL_MASK;
+ val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
+ reg = TAS2552_PDM_CFG;
+ tas2552->pdm_clk_id = clk_id;
+ tas2552->pdm_clk = freq;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clk id: %d\n", clk_id);
+ return -EINVAL;
+ }
- tas2552->mclk = freq;
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ return 0;
+}
+
+static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ unsigned int lsb;
+
+ if (unlikely(!tx_mask)) {
+ dev_err(codec->dev, "tx masks need to be non 0\n");
+ return -EINVAL;
+ }
+
+ /* TDM based on DSP mode requires slots to be adjacent */
+ lsb = __ffs(tx_mask);
+ if ((lsb + 1) != __fls(tx_mask)) {
+ dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+ return -EINVAL;
+ }
+
+ tas2552->tdm_delay = lsb * slot_width;
+
+ /* DOUT in high-impedance on inactive bit clocks */
+ snd_soc_update_bits(codec, TAS2552_DOUT,
+ TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
return 0;
}
static int tas2552_mute(struct snd_soc_dai *dai, int mute)
{
- u8 cfg1_reg;
+ u8 cfg1_reg = 0;
struct snd_soc_codec *codec = dai->codec;
if (mute)
- cfg1_reg = TAS2552_MUTE_MASK;
- else
- cfg1_reg = ~TAS2552_MUTE_MASK;
+ cfg1_reg |= TAS2552_MUTE;
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg);
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
return 0;
}
@@ -269,7 +488,7 @@ static int tas2552_runtime_suspend(struct device *dev)
{
struct tas2552_data *tas2552 = dev_get_drvdata(dev);
- tas2552_sw_shutdown(tas2552, 0);
+ tas2552_sw_shutdown(tas2552, 1);
regcache_cache_only(tas2552->regmap, true);
regcache_mark_dirty(tas2552->regmap);
@@ -287,7 +506,7 @@ static int tas2552_runtime_resume(struct device *dev)
if (tas2552->enable_gpio)
gpiod_set_value(tas2552->enable_gpio, 1);
- tas2552_sw_shutdown(tas2552, 1);
+ tas2552_sw_shutdown(tas2552, 0);
regcache_cache_only(tas2552->regmap, false);
regcache_sync(tas2552->regmap);
@@ -303,8 +522,10 @@ static const struct dev_pm_ops tas2552_pm = {
static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
.hw_params = tas2552_hw_params,
+ .prepare = tas2552_prepare,
.set_sysclk = tas2552_set_dai_sysclk,
.set_fmt = tas2552_set_dai_fmt,
+ .set_tdm_slot = tas2552_set_dai_tdm_slot,
.digital_mute = tas2552_mute,
};
@@ -330,16 +551,22 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
/*
* DAC digital volumes. From -7 to 24 dB in 1 dB steps
*/
-static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
-static const struct snd_kcontrol_new tas2552_snd_controls[] = {
- SOC_SINGLE_TLV("Speaker Driver Playback Volume",
- TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
- SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0),
+static const char * const tas2552_din_source_select[] = {
+ "Muted",
+ "Left",
+ "Right",
+ "Left + Right average",
};
+static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
+ TAS2552_CFG_3, 3,
+ tas2552_din_source_select);
-static const struct reg_default tas2552_init_regs[] = {
- { TAS2552_RESERVED_0D, 0xc0 },
+static const struct snd_kcontrol_new tas2552_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Driver Playback Volume",
+ TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
+ SOC_ENUM("DIN source", tas2552_din_source_enum),
};
static int tas2552_codec_probe(struct snd_soc_codec *codec)
@@ -368,31 +595,20 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
goto probe_fail;
}
- snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK |
- TAS2552_PLL_SRC_BCLK);
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
- TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ);
- snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
- snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
- snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL);
- snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
- TAS2552_APT_THRESH_2_1_7);
-
- ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs,
- ARRAY_SIZE(tas2552_init_regs));
- if (ret != 0) {
- dev_err(codec->dev, "Failed to write init registers: %d\n",
- ret);
- goto patch_fail;
- }
+ TAS2552_DIN_SRC_SEL_AVG_L_R);
+ snd_soc_write(codec, TAS2552_OUTPUT_DATA,
+ TAS2552_PDM_DATA_SEL_V_I |
+ TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
+ snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
+ TAS2552_APT_THRESH_20_17);
- snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
- TAS2552_APT_EN | TAS2552_LIM_EN);
+ snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
+ TAS2552_LIM_EN);
return 0;
-patch_fail:
- pm_runtime_put(codec->dev);
probe_fail:
if (tas2552->enable_gpio)
gpiod_set_value(tas2552->enable_gpio, 0);
@@ -454,6 +670,8 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
.remove = tas2552_codec_remove,
.suspend = tas2552_suspend,
.resume = tas2552_resume,
+ .ignore_pmdown_time = true,
+
.controls = tas2552_snd_controls,
.num_controls = ARRAY_SIZE(tas2552_snd_controls),
.dapm_widgets = tas2552_dapm_widgets,
@@ -485,7 +703,8 @@ static int tas2552_probe(struct i2c_client *client,
if (data == NULL)
return -ENOMEM;
- data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
if (IS_ERR(data->enable_gpio))
return PTR_ERR(data->enable_gpio);
@@ -529,6 +748,7 @@ static int tas2552_probe(struct i2c_client *client,
static int tas2552_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
+ pm_runtime_disable(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index 6cea8f31bf88..5746f8fd0afd 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -19,7 +19,7 @@
#define __TAS2552_H__
/* Register Address Map */
-#define TAS2552_DEVICE_STATUS 0x00
+#define TAS2552_DEVICE_STATUS 0x00
#define TAS2552_CFG_1 0x01
#define TAS2552_CFG_2 0x02
#define TAS2552_CFG_3 0x03
@@ -33,22 +33,26 @@
#define TAS2552_BTIP 0x0b
#define TAS2552_BTS_CTRL 0x0c
#define TAS2552_RESERVED_0D 0x0d
-#define TAS2552_LIMIT_RATE_HYS 0x0e
-#define TAS2552_LIMIT_RELEASE 0x0f
-#define TAS2552_LIMIT_INT_COUNT 0x10
+#define TAS2552_LIMIT_RATE_HYS 0x0e
+#define TAS2552_LIMIT_RELEASE 0x0f
+#define TAS2552_LIMIT_INT_COUNT 0x10
#define TAS2552_PDM_CFG 0x11
#define TAS2552_PGA_GAIN 0x12
-#define TAS2552_EDGE_RATE_CTRL 0x13
-#define TAS2552_BOOST_PT_CTRL 0x14
+#define TAS2552_EDGE_RATE_CTRL 0x13
+#define TAS2552_BOOST_APT_CTRL 0x14
#define TAS2552_VER_NUM 0x16
#define TAS2552_VBAT_DATA 0x19
#define TAS2552_MAX_REG 0x20
/* CFG1 Register Masks */
-#define TAS2552_MUTE_MASK (1 << 2)
-#define TAS2552_SWS_MASK (1 << 1)
-#define TAS2552_WCLK_MASK 0x07
-#define TAS2552_CLASSD_EN_MASK (1 << 7)
+#define TAS2552_DEV_RESET (1 << 0)
+#define TAS2552_SWS (1 << 1)
+#define TAS2552_MUTE (1 << 2)
+#define TAS2552_PLL_SRC_MCLK (0x0 << 4)
+#define TAS2552_PLL_SRC_BCLK (0x1 << 4)
+#define TAS2552_PLL_SRC_IVCLKIN (0x2 << 4)
+#define TAS2552_PLL_SRC_1_8_FIXED (0x3 << 4)
+#define TAS2552_PLL_SRC_MASK TAS2552_PLL_SRC_1_8_FIXED
/* CFG2 Register Masks */
#define TAS2552_CLASSD_EN (1 << 7)
@@ -59,71 +63,84 @@
#define TAS2552_IVSENSE_EN (1 << 1)
/* CFG3 Register Masks */
-#define TAS2552_WORD_CLK_MASK (1 << 7)
-#define TAS2552_BIT_CLK_MASK (1 << 6)
-#define TAS2552_DATA_FORMAT_MASK (0x11 << 2)
-
-#define TAS2552_DAIFMT_I2S_MASK 0xf3
-#define TAS2552_DAIFMT_DSP (1 << 3)
-#define TAS2552_DAIFMT_RIGHT_J (1 << 4)
-#define TAS2552_DAIFMT_LEFT_J (0x11 << 3)
-
-#define TAS2552_PLL_SRC_MCLK 0x00
-#define TAS2552_PLL_SRC_BCLK (1 << 3)
-#define TAS2552_PLL_SRC_IVCLKIN (1 << 4)
-#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3)
-
-#define TAS2552_DIN_SRC_SEL_MUTED 0x00
-#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4)
-#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5)
-#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4)
-
+#define TAS2552_WCLK_FREQ_8KHZ (0x0 << 0)
+#define TAS2552_WCLK_FREQ_11_12KHZ (0x1 << 0)
+#define TAS2552_WCLK_FREQ_16KHZ (0x2 << 0)
+#define TAS2552_WCLK_FREQ_22_24KHZ (0x3 << 0)
+#define TAS2552_WCLK_FREQ_32KHZ (0x4 << 0)
+#define TAS2552_WCLK_FREQ_44_48KHZ (0x5 << 0)
+#define TAS2552_WCLK_FREQ_88_96KHZ (0x6 << 0)
+#define TAS2552_WCLK_FREQ_176_192KHZ (0x7 << 0)
+#define TAS2552_WCLK_FREQ_MASK TAS2552_WCLK_FREQ_176_192KHZ
+#define TAS2552_DIN_SRC_SEL_MUTED (0x0 << 3)
+#define TAS2552_DIN_SRC_SEL_LEFT (0x1 << 3)
+#define TAS2552_DIN_SRC_SEL_RIGHT (0x2 << 3)
+#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x3 << 3)
#define TAS2552_PDM_IN_SEL (1 << 5)
#define TAS2552_I2S_OUT_SEL (1 << 6)
-#define TAS2552_ANALOG_IN_SEL (1 << 7)
-
-/* CFG3 WCLK Dividers */
-#define TAS2552_8KHZ 0x00
-#define TAS2552_11_12KHZ (1 << 1)
-#define TAS2552_16KHZ (1 << 2)
-#define TAS2552_22_24KHZ (1 << 3)
-#define TAS2552_32KHZ (1 << 4)
-#define TAS2552_44_48KHZ (1 << 5)
-#define TAS2552_88_96KHZ (1 << 6)
-#define TAS2552_176_192KHZ (1 << 7)
+#define TAS2552_ANALOG_IN_SEL (1 << 7)
+
+/* DOUT Register Masks */
+#define TAS2552_SDOUT_TRISTATE (1 << 2)
+
+/* Serial Interface Control Register Masks */
+#define TAS2552_WORDLENGTH_16BIT (0x0 << 0)
+#define TAS2552_WORDLENGTH_20BIT (0x1 << 0)
+#define TAS2552_WORDLENGTH_24BIT (0x2 << 0)
+#define TAS2552_WORDLENGTH_32BIT (0x3 << 0)
+#define TAS2552_WORDLENGTH_MASK TAS2552_WORDLENGTH_32BIT
+#define TAS2552_DATAFORMAT_I2S (0x0 << 2)
+#define TAS2552_DATAFORMAT_DSP (0x1 << 2)
+#define TAS2552_DATAFORMAT_RIGHT_J (0x2 << 2)
+#define TAS2552_DATAFORMAT_LEFT_J (0x3 << 2)
+#define TAS2552_DATAFORMAT_MASK TAS2552_DATAFORMAT_LEFT_J
+#define TAS2552_CLKSPERFRAME_32 (0x0 << 4)
+#define TAS2552_CLKSPERFRAME_64 (0x1 << 4)
+#define TAS2552_CLKSPERFRAME_128 (0x2 << 4)
+#define TAS2552_CLKSPERFRAME_256 (0x3 << 4)
+#define TAS2552_CLKSPERFRAME_MASK TAS2552_CLKSPERFRAME_256
+#define TAS2552_BCLKDIR (1 << 6)
+#define TAS2552_WCLKDIR (1 << 7)
/* OUTPUT_DATA register */
-#define TAS2552_PDM_DATA_I 0x00
-#define TAS2552_PDM_DATA_V (1 << 6)
-#define TAS2552_PDM_DATA_I_V (1 << 7)
-#define TAS2552_PDM_DATA_V_I (0x11 << 6)
+#define TAS2552_DATA_OUT_I_DATA (0x0)
+#define TAS2552_DATA_OUT_V_DATA (0x1)
+#define TAS2552_DATA_OUT_VBAT_DATA (0x2)
+#define TAS2552_DATA_OUT_VBOOST_DATA (0x3)
+#define TAS2552_DATA_OUT_PGA_GAIN (0x4)
+#define TAS2552_DATA_OUT_IV_DATA (0x5)
+#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN (0x6)
+#define TAS2552_DATA_OUT_DISABLED (0x7)
+#define TAS2552_L_DATA_OUT(x) ((x) << 0)
+#define TAS2552_R_DATA_OUT(x) ((x) << 3)
+#define TAS2552_PDM_DATA_SEL_I (0x0 << 6)
+#define TAS2552_PDM_DATA_SEL_V (0x1 << 6)
+#define TAS2552_PDM_DATA_SEL_I_V (0x2 << 6)
+#define TAS2552_PDM_DATA_SEL_V_I (0x3 << 6)
+#define TAS2552_PDM_DATA_SEL_MASK TAS2552_PDM_DATA_SEL_V_I
/* PDM CFG Register */
-#define TAS2552_PDM_DATA_ES_RISE 0x4
-
-#define TAS2552_PDM_PLL_CLK_SEL 0x00
-#define TAS2552_PDM_IV_CLK_SEL (1 << 1)
-#define TAS2552_PDM_BCLK_SEL (1 << 2)
-#define TAS2552_PDM_MCLK_SEL (1 << 3)
-
-/* Boost pass-through register */
-#define TAS2552_APT_DELAY_50 0x00
-#define TAS2552_APT_DELAY_75 (1 << 1)
-#define TAS2552_APT_DELAY_125 (1 << 2)
-#define TAS2552_APT_DELAY_200 (1 << 3)
-
-#define TAS2552_APT_THRESH_2_5 0x00
-#define TAS2552_APT_THRESH_1_7 (1 << 3)
-#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4)
-#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2)
+#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0)
+#define TAS2552_PDM_CLK_SEL_IVCLKIN (0x1 << 0)
+#define TAS2552_PDM_CLK_SEL_BCLK (0x2 << 0)
+#define TAS2552_PDM_CLK_SEL_MCLK (0x3 << 0)
+#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK
+#define TAS2552_PDM_DATA_ES (1 << 2)
+
+/* Boost Auto-pass through register */
+#define TAS2552_APT_DELAY_50 (0x0 << 0)
+#define TAS2552_APT_DELAY_75 (0x1 << 0)
+#define TAS2552_APT_DELAY_125 (0x2 << 0)
+#define TAS2552_APT_DELAY_200 (0x3 << 0)
+#define TAS2552_APT_THRESH_05_02 (0x0 << 2)
+#define TAS2552_APT_THRESH_10_07 (0x1 << 2)
+#define TAS2552_APT_THRESH_14_11 (0x2 << 2)
+#define TAS2552_APT_THRESH_20_17 (0x3 << 2)
/* PLL Control Register */
-#define TAS2552_245MHZ_CLK 24576000
-#define TAS2552_225MHZ_CLK 22579200
-#define TAS2552_PLL_J_MASK 0x7f
-#define TAS2552_PLL_D_UPPER_MASK 0x3f
-#define TAS2552_PLL_D_LOWER_MASK 0xff
-#define TAS2552_PLL_BYPASS_MASK 0x80
-#define TAS2552_PLL_BYPASS 0x80
+#define TAS2552_PLL_J_MASK 0x7f
+#define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f)
+#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff)
+#define TAS2552_PLL_BYPASS (1 << 7)
#endif
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
new file mode 100644
index 000000000000..85bcc374c8e8
--- /dev/null
+++ b/sound/soc/codecs/tas571x.c
@@ -0,0 +1,514 @@
+/*
+ * TAS571x amplifier audio driver
+ *
+ * Copyright (C) 2015 Google, Inc.
+ * Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/stddef.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "tas571x.h"
+
+#define TAS571X_MAX_SUPPLIES 6
+
+struct tas571x_chip {
+ const char *const *supply_names;
+ int num_supply_names;
+ const struct snd_kcontrol_new *controls;
+ int num_controls;
+ const struct regmap_config *regmap_config;
+ int vol_reg_size;
+};
+
+struct tas571x_private {
+ const struct tas571x_chip *chip;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES];
+ struct clk *mclk;
+ unsigned int format;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *pdn_gpio;
+ struct snd_soc_codec_driver codec_driver;
+};
+
+static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
+{
+ switch (reg) {
+ case TAS571X_MVOL_REG:
+ case TAS571X_CH1_VOL_REG:
+ case TAS571X_CH2_VOL_REG:
+ return priv->chip->vol_reg_size;
+ default:
+ return 1;
+ }
+}
+
+static int tas571x_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ struct tas571x_private *priv = i2c_get_clientdata(client);
+ unsigned int i, size;
+ uint8_t buf[5];
+ int ret;
+
+ size = tas571x_register_size(priv, reg);
+ buf[0] = reg;
+
+ for (i = size; i >= 1; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 1);
+ if (ret == size + 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int tas571x_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct tas571x_private *priv = i2c_get_clientdata(client);
+ uint8_t send_buf, recv_buf[4];
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ unsigned int i;
+ int ret;
+
+ size = tas571x_register_size(priv, reg);
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
+static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int tas571x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ u32 val;
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = 0x00;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x03;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x06;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (params_width(params) >= 24)
+ val += 2;
+ else if (params_width(params) >= 20)
+ val += 1;
+
+ return regmap_update_bits(priv->regmap, TAS571X_SDI_REG,
+ TAS571X_SDI_FMT_MASK, val);
+}
+
+static int tas571x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (!IS_ERR(priv->mclk)) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable master clock: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ gpiod_set_value(priv->pdn_gpio, 0);
+ usleep_range(5000, 6000);
+
+ regcache_cache_only(priv->regmap, false);
+ ret = regcache_sync(priv->regmap);
+ if (ret)
+ return ret;
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ regcache_cache_only(priv->regmap, true);
+ gpiod_set_value(priv->pdn_gpio, 1);
+
+ if (!IS_ERR(priv->mclk))
+ clk_disable_unprepare(priv->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tas571x_dai_ops = {
+ .set_fmt = tas571x_set_dai_fmt,
+ .hw_params = tas571x_hw_params,
+};
+
+static const char *const tas5711_supply_names[] = {
+ "AVDD",
+ "DVDD",
+ "PVDD_A",
+ "PVDD_B",
+ "PVDD_C",
+ "PVDD_D",
+};
+
+static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new tas5711_controls[] = {
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG,
+ 0, 0xff, 1, tas5711_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG,
+ TAS571X_CH2_VOL_REG,
+ 0, 0xff, 1, tas5711_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+};
+
+static const struct reg_default tas5711_reg_defaults[] = {
+ { 0x04, 0x05 },
+ { 0x05, 0x40 },
+ { 0x06, 0x00 },
+ { 0x07, 0xff },
+ { 0x08, 0x30 },
+ { 0x09, 0x30 },
+ { 0x1b, 0x82 },
+};
+
+static const struct regmap_config tas5711_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5711_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tas571x_chip tas5711_chip = {
+ .supply_names = tas5711_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5711_supply_names),
+ .controls = tas5711_controls,
+ .num_controls = ARRAY_SIZE(tas5711_controls),
+ .regmap_config = &tas5711_regmap_config,
+ .vol_reg_size = 1,
+};
+
+static const char *const tas5717_supply_names[] = {
+ "AVDD",
+ "DVDD",
+ "HPVDD",
+ "PVDD_AB",
+ "PVDD_CD",
+};
+
+static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0);
+
+static const struct snd_kcontrol_new tas5717_controls[] = {
+ /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG, 1, 0x1ff, 1,
+ tas5717_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG,
+ 1, 0x1ff, 1, tas5717_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+};
+
+static const struct reg_default tas5717_reg_defaults[] = {
+ { 0x04, 0x05 },
+ { 0x05, 0x40 },
+ { 0x06, 0x00 },
+ { 0x07, 0x03ff },
+ { 0x08, 0x00c0 },
+ { 0x09, 0x00c0 },
+ { 0x1b, 0x82 },
+};
+
+static const struct regmap_config tas5717_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5717_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/* This entry is reused for tas5719 as the software interface is identical. */
+static const struct tas571x_chip tas5717_chip = {
+ .supply_names = tas5717_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5717_supply_names),
+ .controls = tas5717_controls,
+ .num_controls = ARRAY_SIZE(tas5717_controls),
+ .regmap_config = &tas5717_regmap_config,
+ .vol_reg_size = 2,
+};
+
+static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT_A"),
+ SND_SOC_DAPM_OUTPUT("OUT_B"),
+ SND_SOC_DAPM_OUTPUT("OUT_C"),
+ SND_SOC_DAPM_OUTPUT("OUT_D"),
+};
+
+static const struct snd_soc_dapm_route tas571x_dapm_routes[] = {
+ { "DACL", NULL, "Playback" },
+ { "DACR", NULL, "Playback" },
+
+ { "OUT_A", NULL, "DACL" },
+ { "OUT_B", NULL, "DACL" },
+ { "OUT_C", NULL, "DACR" },
+ { "OUT_D", NULL, "DACR" },
+};
+
+static const struct snd_soc_codec_driver tas571x_codec = {
+ .set_bias_level = tas571x_set_bias_level,
+ .idle_bias_off = true,
+
+ .dapm_widgets = tas571x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets),
+ .dapm_routes = tas571x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes),
+};
+
+static struct snd_soc_dai_driver tas571x_dai = {
+ .name = "tas571x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tas571x_dai_ops,
+};
+
+static const struct of_device_id tas571x_of_match[];
+
+static int tas571x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tas571x_private *priv;
+ struct device *dev = &client->dev;
+ const struct of_device_id *of_id;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ i2c_set_clientdata(client, priv);
+
+ of_id = of_match_device(tas571x_of_match, dev);
+ if (!of_id) {
+ dev_err(dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+ priv->chip = of_id->data;
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
+ dev_err(dev, "Failed to request mclk: %ld\n",
+ PTR_ERR(priv->mclk));
+ return PTR_ERR(priv->mclk);
+ }
+
+ BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES);
+ for (i = 0; i < priv->chip->num_supply_names; i++)
+ priv->supplies[i].supply = priv->chip->supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names,
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(priv->chip->num_supply_names,
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ priv->regmap = devm_regmap_init(dev, NULL, client,
+ priv->chip->regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->pdn_gpio)) {
+ dev_err(dev, "error requesting pdn_gpio: %ld\n",
+ PTR_ERR(priv->pdn_gpio));
+ return PTR_ERR(priv->pdn_gpio);
+ }
+
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset_gpio)) {
+ dev_err(dev, "error requesting reset_gpio: %ld\n",
+ PTR_ERR(priv->reset_gpio));
+ return PTR_ERR(priv->reset_gpio);
+ } else if (priv->reset_gpio) {
+ /* pulse the active low reset line for ~100us */
+ usleep_range(100, 200);
+ gpiod_set_value(priv->reset_gpio, 0);
+ usleep_range(12000, 20000);
+ }
+
+ ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG,
+ TAS571X_SYS_CTRL_2_SDN_MASK, 0);
+ if (ret)
+ return ret;
+
+ memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
+ priv->codec_driver.controls = priv->chip->controls;
+ priv->codec_driver.num_controls = priv->chip->num_controls;
+
+ if (priv->chip->vol_reg_size == 2) {
+ /*
+ * The master volume defaults to 0x3ff (mute), but we ignore
+ * (zero) the LSB because the hardware step size is 0.125 dB
+ * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB.
+ */
+ ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
+ if (ret)
+ return ret;
+ }
+
+ regcache_cache_only(priv->regmap, true);
+ gpiod_set_value(priv->pdn_gpio, 1);
+
+ return snd_soc_register_codec(&client->dev, &priv->codec_driver,
+ &tas571x_dai, 1);
+}
+
+static int tas571x_i2c_remove(struct i2c_client *client)
+{
+ struct tas571x_private *priv = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
+
+ return 0;
+}
+
+static const struct of_device_id tas571x_of_match[] = {
+ { .compatible = "ti,tas5711", .data = &tas5711_chip, },
+ { .compatible = "ti,tas5717", .data = &tas5717_chip, },
+ { .compatible = "ti,tas5719", .data = &tas5717_chip, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas571x_of_match);
+
+static const struct i2c_device_id tas571x_i2c_id[] = {
+ { "tas5711", 0 },
+ { "tas5717", 0 },
+ { "tas5719", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
+
+static struct i2c_driver tas571x_i2c_driver = {
+ .driver = {
+ .name = "tas571x",
+ .of_match_table = of_match_ptr(tas571x_of_match),
+ },
+ .probe = tas571x_i2c_probe,
+ .remove = tas571x_i2c_remove,
+ .id_table = tas571x_i2c_id,
+};
+module_i2c_driver(tas571x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TAS571x driver");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
new file mode 100644
index 000000000000..0aee471232cd
--- /dev/null
+++ b/sound/soc/codecs/tas571x.h
@@ -0,0 +1,33 @@
+/*
+ * TAS571x amplifier audio driver
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _TAS571X_H
+#define _TAS571X_H
+
+/* device registers */
+#define TAS571X_SDI_REG 0x04
+#define TAS571X_SDI_FMT_MASK 0x0f
+
+#define TAS571X_SYS_CTRL_2_REG 0x05
+#define TAS571X_SYS_CTRL_2_SDN_MASK 0x40
+
+#define TAS571X_SOFT_MUTE_REG 0x06
+#define TAS571X_SOFT_MUTE_CH1_SHIFT 0
+#define TAS571X_SOFT_MUTE_CH2_SHIFT 1
+#define TAS571X_SOFT_MUTE_CH3_SHIFT 2
+
+#define TAS571X_MVOL_REG 0x07
+#define TAS571X_CH1_VOL_REG 0x08
+#define TAS571X_CH2_VOL_REG 0x09
+
+#define TAS571X_OSC_TRIM_REG 0x1b
+
+#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index cc17e7e5126e..cd8c02b6e4de 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -506,7 +506,6 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, TLV320AIC23_PWR, 0x1ff);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index c86dd9aae157..c4c960f592a1 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -646,7 +646,7 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec)
static int aic31xx_add_widgets(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
int ret = 0;
@@ -1027,17 +1027,17 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__,
- codec->dapm.bias_level, level);
+ snd_soc_codec_get_bias_level(codec), level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
aic31xx_clk_on(codec);
break;
case SND_SOC_BIAS_STANDBY:
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_OFF:
aic31xx_power_on(codec);
break;
@@ -1049,11 +1049,10 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
}
break;
case SND_SOC_BIAS_OFF:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
aic31xx_power_off(codec);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 015467ed606b..ad6cb90e5f9b 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -564,7 +564,6 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 51c4713ac6e3..a7cf19b53fb2 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -147,6 +147,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -179,7 +180,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
update.mask = mask;
update.val = val;
- snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect,
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, connect,
&update);
}
@@ -979,7 +980,7 @@ static const struct snd_soc_dapm_route intercon_3007[] = {
static int aic3x_add_widgets(struct snd_soc_codec *codec)
{
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
switch (aic3x->model) {
case AIC3X_MODEL_3X:
@@ -1384,7 +1385,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY &&
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY &&
aic3x->master) {
/* enable pll */
snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
@@ -1394,7 +1395,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (!aic3x->power)
aic3x_set_power(codec, 1);
- if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE &&
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE &&
aic3x->master) {
/* disable pll */
snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
@@ -1406,7 +1407,6 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
aic3x_set_power(codec, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 4e3e607dec13..d67a311f0e75 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -633,7 +633,7 @@ static int dac33_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) {
/* Coming from OFF, switch on the codec */
ret = dac33_hard_power(codec, 1);
if (ret != 0)
@@ -644,14 +644,13 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
/* Do not power off, when the codec is already off */
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
return 0;
ret = dac33_hard_power(codec, 0);
if (ret != 0)
return ret;
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 9fd80ac1897f..12232d7db4c5 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -254,12 +254,13 @@ static const struct regmap_config ts3a227e_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults),
};
-static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np)
+static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e,
+ struct device *dev)
{
u32 micbias;
int err;
- err = of_property_read_u32(np, "ti,micbias", &micbias);
+ err = device_property_read_u32(dev, "ti,micbias", &micbias);
if (!err) {
regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3,
MICBIAS_SETTING_MASK,
@@ -287,12 +288,10 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(ts3a227e->regmap))
return PTR_ERR(ts3a227e->regmap);
- if (dev->of_node) {
- ret = ts3a227e_parse_dt(ts3a227e, dev->of_node);
- if (ret) {
- dev_err(dev, "Failed to parse device tree: %d\n", ret);
- return ret;
- }
+ ret = ts3a227e_parse_device_property(ts3a227e, dev);
+ if (ret) {
+ dev_err(dev, "Failed to parse device property: %d\n", ret);
+ return ret;
}
ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt,
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index d04693e9cf9f..90f5f04eca2d 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1588,14 +1588,13 @@ static int twl4030_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)
twl4030_codec_enable(codec, 1);
break;
case SND_SOC_BIAS_OFF:
twl4030_codec_enable(codec, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index aeec27b6f1af..4cad8929d262 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -533,7 +533,7 @@ static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
if (snd_soc_dapm_get_pin_status(dapm, "EP"))
return -1; /* -1dB */
@@ -853,8 +853,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1123,14 +1121,15 @@ static int twl6040_probe(struct snd_soc_codec *codec)
mutex_init(&priv->mutex);
ret = request_threaded_irq(priv->plug_irq, NULL,
- twl6040_audio_handler, IRQF_NO_SUSPEND,
+ twl6040_audio_handler,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
"twl6040_irq_plug", codec);
if (ret) {
dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
return ret;
}
- twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
twl6040_init_chip(codec);
return 0;
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index f883308c00de..913edf283239 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -350,7 +350,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
pd->power(0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -478,6 +477,7 @@ 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;
const struct snd_soc_dapm_widget *widgets;
@@ -526,7 +526,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
}
- ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets);
+ ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
if (ret) {
printk(KERN_ERR "%s failed to register dapm controls: %d",
__func__, ret);
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index c3c33bd0df1c..6e159f59d219 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -590,9 +590,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
int reg;
struct uda1380_platform_data *pdata = codec->dev->platform_data;
- if (codec->dapm.bias_level == level)
- return 0;
-
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
@@ -600,7 +597,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
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 (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
@@ -623,7 +620,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++)
set_bit(reg - 0x10, &uda1380_cache_dirty);
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index f37989ec7cba..6560a66b3f35 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -751,13 +751,13 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE)
wm0010_boot(codec);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
mutex_lock(&wm0010->lock);
wm0010_halt(codec);
mutex_unlock(&wm0010->lock);
@@ -767,8 +767,6 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index 8011f75fb6cb..048f00568260 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -61,8 +61,6 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 5a9da28f4f33..c83083285e53 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -1555,7 +1555,7 @@ static int wm2200_probe(struct snd_soc_codec *codec)
wm2200->codec = codec;
- ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2);
+ ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2);
if (ret != 0)
return ret;
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 96740379b711..4c10cd88c1af 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2101,7 +2101,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100)
int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
if (jack) {
wm5100->jack = jack;
@@ -2336,6 +2336,7 @@ static void wm5100_free_gpio(struct i2c_client *i2c)
static int wm5100_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct i2c_client *i2c = to_i2c_client(codec->dev);
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
int ret, i;
@@ -2353,8 +2354,7 @@ static int wm5100_probe(struct snd_soc_codec *codec)
/* TODO: check if we're symmetric */
if (i2c->irq)
- snd_soc_dapm_new_controls(&codec->dapm,
- wm5100_dapm_widgets_noirq,
+ snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq,
ARRAY_SIZE(wm5100_dapm_widgets_noirq));
if (wm5100->pdata.hp_pol) {
@@ -2570,11 +2570,13 @@ 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,
+ wm5100_edge_irq,
+ irq_flags | IRQF_ONESHOT,
"wm5100", wm5100);
else
ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq,
- irq_flags, "wm5100",
+ irq_flags | IRQF_ONESHOT,
+ "wm5100",
wm5100);
if (ret != 0) {
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 0c6d1bc0526e..d097f09e50f2 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -42,7 +42,7 @@ struct wm5102_priv {
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);
-static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
static const struct wm_adsp_region wm5102_dsp1_regions[] = {
@@ -605,12 +605,56 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
regmap_write_async(regmap, patch[i].reg,
patch[i].def);
break;
+ case SND_SOC_DAPM_PRE_PMD:
+ break;
+ default:
+ return 0;
+ }
+
+ return arizona_dvfs_sysclk_ev(w, kcontrol, event);
+}
+
+static int wm5102_adsp_power_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 *arizona = dev_get_drvdata(codec->dev->parent);
+ unsigned int v;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to read SYSCLK state: %d\n", ret);
+ return -EIO;
+ }
+
+ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ if (v >= 3) {
+ ret = arizona_dvfs_up(codec, ARIZONA_DVFS_ADSP1_RQ);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to raise DVFS: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ ret = arizona_dvfs_down(codec, ARIZONA_DVFS_ADSP1_RQ);
+ if (ret)
+ dev_warn(codec->dev,
+ "Failed to lower DVFS: %d\n", ret);
+ break;
default:
break;
}
- return 0;
+ return wm_adsp2_early_event(w, kcontrol, event);
}
static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
@@ -1036,7 +1080,8 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
- 0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+ 0, wm5102_sysclk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
@@ -1367,7 +1412,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
-WM_ADSP2("DSP1", 0),
+WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1827,19 +1872,25 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
static int wm5102_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2);
- if (ret != 0)
+ ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_codec_controls(codec,
+ arizona_adsp2_rate_controls, 1);
+ if (ret)
return ret;
arizona_init_spk(codec);
arizona_init_gpio(codec);
- snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
- priv->core.arizona->dapm = &codec->dapm;
+ priv->core.arizona->dapm = dapm;
return 0;
}
@@ -1848,6 +1899,8 @@ static int wm5102_codec_remove(struct snd_soc_codec *codec)
{
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
+
priv->core.arizona->dapm = NULL;
return 0;
@@ -1909,6 +1962,8 @@ static int wm5102_probe(struct platform_device *pdev)
wm5102->core.arizona = arizona;
wm5102->core.num_inputs = 6;
+ arizona_init_dvfs(&wm5102->core);
+
wm5102->core.adsp[0].part = "wm5102";
wm5102->core.adsp[0].num = 1;
wm5102->core.adsp[0].type = WMFW_ADSP2;
@@ -1918,7 +1973,7 @@ static int wm5102_probe(struct platform_device *pdev)
wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
- ret = wm_adsp2_init(&wm5102->core.adsp[0], true);
+ ret = wm_adsp2_init(&wm5102->core.adsp[0]);
if (ret != 0)
return ret;
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index fbaeddb3e903..709fcc6169d8 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -167,7 +167,7 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
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);
-static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
#define WM5110_NG_SRC(name, base) \
@@ -1598,22 +1598,29 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
- int ret;
+ int i, ret;
- priv->core.arizona->dapm = &codec->dapm;
+ priv->core.arizona->dapm = dapm;
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
- ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8);
- if (ret != 0)
- return ret;
+ for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+ ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
+ if (ret)
+ return ret;
+ }
- snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+ ret = snd_soc_add_codec_controls(codec,
+ arizona_adsp2_rate_controls,
+ WM5110_NUM_ADSP);
+ if (ret)
+ return ret;
- priv->core.arizona->dapm = &codec->dapm;
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
return 0;
}
@@ -1621,6 +1628,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < WM5110_NUM_ADSP; ++i)
+ wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
priv->core.arizona->dapm = NULL;
@@ -1697,7 +1708,7 @@ static int wm5110_probe(struct platform_device *pdev)
wm5110->core.adsp[i].num_mems
= ARRAY_SIZE(wm5110_dsp1_regions);
- ret = wm_adsp2_init(&wm5110->core.adsp[i], false);
+ ret = wm_adsp2_init(&wm5110->core.adsp[i]);
if (ret != 0)
return ret;
}
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index c65e5a75fc1a..41c62c1e62db 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1102,7 +1102,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
priv->supplies);
if (ret != 0)
@@ -1235,7 +1235,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
priv->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b0d84e552fca..d7555085e7f4 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1145,7 +1145,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(power),
&power[0]);
if (ret != 0) {
@@ -1232,7 +1232,6 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 8736ad094b24..dac5beb4d023 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -519,7 +519,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
regcache_sync(wm8510->regmap);
/* Initial cap charge at VMID 5k */
@@ -538,7 +538,6 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index b1cc94f5fc4b..43ea8ae5f871 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -113,6 +113,15 @@ static struct {
{ 7, 1152 },
};
+static struct {
+ int value;
+ int ratio;
+} bclk_ratios[WM8523_NUM_RATES] = {
+ { 2, 32 },
+ { 3, 64 },
+ { 4, 128 },
+};
+
static int wm8523_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -162,6 +171,23 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
aifctrl2 &= ~WM8523_SR_MASK;
aifctrl2 |= lrclk_ratios[i].value;
+ if (aifctrl1 & WM8523_AIF_MSTR) {
+ /* Find a fs->bclk ratio */
+ for (i = 0; i < ARRAY_SIZE(bclk_ratios); i++)
+ if (params_width(params) * 2 <= bclk_ratios[i].ratio)
+ break;
+
+ if (i == ARRAY_SIZE(bclk_ratios)) {
+ dev_err(codec->dev,
+ "No matching BCLK/fs ratio for word length %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ aifctrl2 &= ~WM8523_BCLKDIV_MASK;
+ aifctrl2 |= bclk_ratios[i].value << WM8523_BCLKDIV_SHIFT;
+ }
+
aifctrl1 &= ~WM8523_WL_MASK;
switch (params_width(params)) {
case 16:
@@ -308,7 +334,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
wm8523->supplies);
if (ret != 0) {
@@ -344,7 +370,6 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec,
wm8523->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 0a887c5ec83a..759a7928ac3e 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -795,7 +795,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
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) {
/* Power up and get individual control of the DACs */
snd_soc_update_bits(codec, WM8580_PWRDN1,
WM8580_PWRDN1_PWDN |
@@ -812,7 +812,6 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 121e46d53779..cc8251f09f8a 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -310,7 +310,7 @@ static int wm8711_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)
regcache_sync(wm8711->regmap);
snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
@@ -320,7 +320,6 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8711_PWR, 0xffff);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 55c7fb4fc786..f1a173e6ec33 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -170,7 +170,7 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
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) {
/* Power everything up... */
reg = snd_soc_read(codec, WM8728_DACCTL);
snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
@@ -185,7 +185,6 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 2245b6a32f3d..915ea11ad4b6 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -387,6 +387,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
@@ -421,7 +422,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
wm8731->sysclk = freq;
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
return 0;
}
@@ -501,7 +502,7 @@ static int wm8731_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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0)
@@ -523,7 +524,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
regcache_mark_dirty(wm8731->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -599,7 +599,7 @@ static int wm8731_probe(struct snd_soc_codec *codec)
goto err_regulator_enable;
}
- wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index ada9ac1ba2c6..6ad606fd8b69 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -469,7 +469,7 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies),
wm8737->supplies);
if (ret != 0) {
@@ -483,7 +483,8 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
/* Fast VMID ramp at 2*2.5k */
snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
- WM8737_VMIDSEL_MASK, 0x4);
+ WM8737_VMIDSEL_MASK,
+ 2 << WM8737_VMIDSEL_SHIFT);
/* Bring VMID up */
snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT,
@@ -497,7 +498,8 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
/* VMID at 2*300k */
snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
- WM8737_VMIDSEL_MASK, 2);
+ WM8737_VMIDSEL_MASK,
+ 1 << WM8737_VMIDSEL_SHIFT);
break;
@@ -510,7 +512,6 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -560,7 +561,7 @@ static int wm8737_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU,
WM8737_RVU);
- wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 9e71c768966f..b34623786e35 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
/* codec private data */
struct wm8741_priv {
+ struct wm8741_platform_data pdata;
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
unsigned int sysclk;
@@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec)
static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
-static const struct snd_kcontrol_new wm8741_snd_controls[] = {
+static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = {
SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
};
+static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = {
+SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
+ 1, 255, 1, dac_tlv_fine),
+SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
+ 0, 511, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = {
+SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION,
+ 1, 255, 1, dac_tlv_fine),
+SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION,
+ 0, 511, 1, dac_tlv),
+};
+
static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
@@ -110,18 +125,6 @@ static const struct snd_soc_dapm_route wm8741_dapm_routes[] = {
{ "VOUTRN", NULL, "DACR" },
};
-static struct {
- int value;
- int ratio;
-} lrclk_ratios[WM8741_NUM_RATES] = {
- { 1, 128 },
- { 2, 192 },
- { 3, 256 },
- { 4, 384 },
- { 5, 512 },
- { 6, 768 },
-};
-
static const unsigned int rates_11289[] = {
44100, 88200,
};
@@ -194,25 +197,16 @@ static const struct snd_pcm_hw_constraint_list constraints_36864 = {
.list = rates_36864,
};
-
static int wm8741_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
- /* The set of sample rates that can be supported depends on the
- * MCLK supplied to the CODEC - enforce this.
- */
- if (!wm8741->sysclk) {
- dev_err(codec->dev,
- "No MCLK configured, call set_sysclk() on init\n");
- return -EINVAL;
- }
-
- snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- wm8741->sysclk_constraints);
+ if (wm8741->sysclk)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ wm8741->sysclk_constraints);
return 0;
}
@@ -226,17 +220,24 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream,
u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
int i;
- /* Find a supported LRCLK ratio */
- for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
- if (wm8741->sysclk / params_rate(params) ==
- lrclk_ratios[i].ratio)
+ /* The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC - enforce this.
+ */
+ if (!wm8741->sysclk) {
+ dev_err(codec->dev,
+ "No MCLK configured, call set_sysclk() on init or in hw_params\n");
+ return -EINVAL;
+ }
+
+ /* Find a supported LRCLK rate */
+ for (i = 0; i < wm8741->sysclk_constraints->count; i++) {
+ if (wm8741->sysclk_constraints->list[i] == params_rate(params))
break;
}
- /* Should never happen, should be handled by constraints */
- if (i == ARRAY_SIZE(lrclk_ratios)) {
- dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
- wm8741->sysclk / params_rate(params));
+ if (i == wm8741->sysclk_constraints->count) {
+ dev_err(codec->dev, "LRCLK %d unsupported with MCLK %d\n",
+ params_rate(params), wm8741->sysclk);
return -EINVAL;
}
@@ -259,8 +260,8 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d",
- params_width(params));
+ dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d, rate param = %d",
+ params_width(params), params_rate(params));
snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
return 0;
@@ -275,6 +276,11 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
switch (freq) {
+ case 0:
+ wm8741->sysclk_constraints = NULL;
+ wm8741->sysclk = freq;
+ return 0;
+
case 11289600:
wm8741->sysclk_constraints = &constraints_11289;
wm8741->sysclk = freq;
@@ -398,7 +404,7 @@ static struct snd_soc_dai_driver wm8741_dai = {
.name = "wm8741",
.playback = {
.stream_name = "Playback",
- .channels_min = 2, /* Mono modes not yet supported */
+ .channels_min = 2,
.channels_max = 2,
.rates = WM8741_RATES,
.formats = WM8741_FORMATS,
@@ -416,6 +422,65 @@ static int wm8741_resume(struct snd_soc_codec *codec)
#define wm8741_resume NULL
#endif
+static int wm8741_configure(struct snd_soc_codec *codec)
+{
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+ /* Configure differential mode */
+ switch (wm8741->pdata.diff_mode) {
+ case WM8741_DIFF_MODE_STEREO:
+ case WM8741_DIFF_MODE_STEREO_REVERSED:
+ case WM8741_DIFF_MODE_MONO_LEFT:
+ case WM8741_DIFF_MODE_MONO_RIGHT:
+ snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2,
+ WM8741_DIFF_MASK,
+ wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Change some default settings - latch VU */
+ snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+ WM8741_UPDATELL, WM8741_UPDATELL);
+ snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+ WM8741_UPDATELM, WM8741_UPDATELM);
+ snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+ WM8741_UPDATERL, WM8741_UPDATERL);
+ snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
+ WM8741_UPDATERM, WM8741_UPDATERM);
+
+ return 0;
+}
+
+static int wm8741_add_controls(struct snd_soc_codec *codec)
+{
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+ switch (wm8741->pdata.diff_mode) {
+ case WM8741_DIFF_MODE_STEREO:
+ case WM8741_DIFF_MODE_STEREO_REVERSED:
+ snd_soc_add_codec_controls(codec,
+ wm8741_snd_controls_stereo,
+ ARRAY_SIZE(wm8741_snd_controls_stereo));
+ break;
+ case WM8741_DIFF_MODE_MONO_LEFT:
+ snd_soc_add_codec_controls(codec,
+ wm8741_snd_controls_mono_left,
+ ARRAY_SIZE(wm8741_snd_controls_mono_left));
+ break;
+ case WM8741_DIFF_MODE_MONO_RIGHT:
+ snd_soc_add_codec_controls(codec,
+ wm8741_snd_controls_mono_right,
+ ARRAY_SIZE(wm8741_snd_controls_mono_right));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wm8741_probe(struct snd_soc_codec *codec)
{
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
@@ -434,15 +499,17 @@ static int wm8741_probe(struct snd_soc_codec *codec)
goto err_enable;
}
- /* Change some default settings - latch VU */
- snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
- WM8741_UPDATELL, WM8741_UPDATELL);
- snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
- WM8741_UPDATELM, WM8741_UPDATELM);
- snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
- WM8741_UPDATERL, WM8741_UPDATERL);
- snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
- WM8741_UPDATERM, WM8741_UPDATERM);
+ ret = wm8741_configure(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to change default settings\n");
+ goto err_enable;
+ }
+
+ ret = wm8741_add_controls(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to add controls\n");
+ goto err_enable;
+ }
dev_dbg(codec->dev, "Successful registration\n");
return ret;
@@ -467,8 +534,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
.remove = wm8741_remove,
.resume = wm8741_resume,
- .controls = wm8741_snd_controls,
- .num_controls = ARRAY_SIZE(wm8741_snd_controls),
.dapm_widgets = wm8741_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
.dapm_routes = wm8741_dapm_routes,
@@ -493,6 +558,23 @@ static const struct regmap_config wm8741_regmap = {
.readable_reg = wm8741_readable,
};
+static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
+{
+ const struct wm8741_platform_data *pdata = dev_get_platdata(dev);
+ u32 diff_mode;
+
+ if (dev->of_node) {
+ if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode)
+ >= 0)
+ wm8741->pdata.diff_mode = diff_mode;
+ } else {
+ if (pdata != NULL)
+ memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata));
+ }
+
+ return 0;
+}
+
#if IS_ENABLED(CONFIG_I2C)
static int wm8741_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
@@ -522,6 +604,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ ret = wm8741_set_pdata(&i2c->dev, wm8741);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, wm8741);
ret = snd_soc_register_codec(&i2c->dev,
@@ -582,6 +670,12 @@ static int wm8741_spi_probe(struct spi_device *spi)
return ret;
}
+ ret = wm8741_set_pdata(&spi->dev, wm8741);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to set pdata: %d\n", ret);
+ return ret;
+ }
+
spi_set_drvdata(spi, wm8741);
ret = snd_soc_register_codec(&spi->dev,
diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h
index 56c1b1d4a681..c8835f65f342 100644
--- a/sound/soc/codecs/wm8741.h
+++ b/sound/soc/codecs/wm8741.h
@@ -194,6 +194,12 @@
#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */
+/* DIFF field values */
+#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */
+#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */
+#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */
+#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */
+
/*
* R32 (0x20) - ADDITONAL_CONTROL_1
*/
@@ -208,4 +214,8 @@
#define WM8741_SYSCLK 0
+struct wm8741_platform_data {
+ u32 diff_mode; /* Differential Output Mode */
+};
+
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index eb0a1644ba11..56d89b0865fa 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -634,7 +634,7 @@ static int wm8750_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) {
snd_soc_cache_sync(codec);
/* Set VMID to 5k */
@@ -651,7 +651,6 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8750_PWR1, 0x0001);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index c50a5959345f..feb2997a377a 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1352,7 +1352,7 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
flush_delayed_work(&wm8753->charge_work);
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) {
/* set vmid to 5k for quick power up */
snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
schedule_delayed_work(&wm8753->charge_work,
@@ -1367,7 +1367,6 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8753_PWR1, 0x0001);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index 53e977da2f86..66c1f151071d 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -510,7 +510,7 @@ static int wm8770_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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
wm8770->supplies);
if (ret) {
@@ -534,7 +534,6 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index c13050b77931..ece9b4456767 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -344,7 +344,7 @@ static int wm8776_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) {
regcache_sync(wm8776->regmap);
/* Disable the global powerdown; DAPM does the rest */
@@ -357,7 +357,6 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 1e403f67cf16..c195c2e8af07 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -162,7 +162,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
unsigned int mask = 1 << e->shift_l;
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 2eb986c19b88..f3759ec5a863 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -998,8 +998,8 @@ static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define WM8900_PCM_FORMATS \
- (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
- SNDRV_PCM_FORMAT_S24_LE)
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops wm8900_dai_ops = {
.hw_params = wm8900_hw_params,
@@ -1049,7 +1049,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
/* Charge capacitors if initial power up */
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
/* STARTUP_BIAS_ENA on */
snd_soc_write(codec, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA);
@@ -1117,7 +1117,6 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
WM8900_REG_POWER2_SYSCLK_ENA);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1138,7 +1137,7 @@ static int wm8900_suspend(struct snd_soc_codec *codec)
wm8900->fll_out = fll_out;
wm8900->fll_in = fll_in;
- wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1156,7 +1155,7 @@ static int wm8900_resume(struct snd_soc_codec *codec)
return ret;
}
- wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Restart the FLL? */
if (wm8900->fll_out) {
@@ -1189,7 +1188,7 @@ static int wm8900_probe(struct snd_soc_codec *codec)
wm8900_reset(codec);
/* Turn the chip on */
- wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the volume update bits */
snd_soc_update_bits(codec, WM8900_REG_LINVOL, 0x100, 0x100);
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 04b04f8e147c..b5322c1544fb 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1105,7 +1105,7 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
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) {
snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
WM8903_POBCTRL | WM8903_ISEL_MASK |
WM8903_STARTUP_BIAS_ENA |
@@ -1200,8 +1200,6 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
index db949311c0f2..0bb4a647755d 100644
--- a/sound/soc/codecs/wm8903.h
+++ b/sound/soc/codecs/wm8903.h
@@ -172,7 +172,7 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec,
#define WM8903_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
#define WM8903_VMID_RES_50K 2
-#define WM8903_VMID_RES_250K 3
+#define WM8903_VMID_RES_250K 4
#define WM8903_VMID_RES_5K 6
/*
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 215e93c1ddf0..265a4a58a2d1 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1168,7 +1168,7 @@ static const struct snd_soc_dapm_route wm8912_intercon[] = {
static int wm8904_add_widgets(struct snd_soc_codec *codec)
{
struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets,
ARRAY_SIZE(wm8904_core_dapm_widgets));
@@ -1852,7 +1852,7 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
wm8904->supplies);
if (ret != 0) {
@@ -1907,7 +1907,6 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,
clk_disable_unprepare(wm8904->mclk);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index e4142b4309eb..98ef0ba5c2a4 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -492,7 +492,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
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) {
ret = regcache_sync(wm8940->regmap);
if (ret < 0) {
dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
@@ -510,8 +510,6 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return ret;
}
@@ -707,7 +705,7 @@ static int wm8940_probe(struct snd_soc_codec *codec)
return ret;
}
- wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
if (ret < 0)
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 00bec915d652..2d591c24704b 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -298,7 +298,7 @@ static int wm8955_configure_clocking(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
WM8955_K_17_9_MASK,
(pll.k >> 9) & WM8955_K_17_9_MASK);
- snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+ snd_soc_update_bits(codec, WM8955_PLL_CONTROL_3,
WM8955_K_8_0_MASK,
pll.k & WM8955_K_8_0_MASK);
if (pll.k)
@@ -785,7 +785,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
wm8955->supplies);
if (ret != 0) {
@@ -838,7 +838,6 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
wm8955->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -929,7 +928,7 @@ static int wm8955_probe(struct snd_soc_codec *codec)
WM8955_DMEN, WM8955_DMEN);
}
- wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index e97a7615df85..94c5c4681ce5 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -127,6 +127,8 @@ struct wm8960_priv {
struct snd_soc_dapm_widget *out3;
bool deemph;
int playback_fs;
+ int bclk;
+ int sysclk;
struct wm8960_data pdata;
};
@@ -245,7 +247,7 @@ SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
SOC_ENUM("ADC Polarity", wm8960_enum[0]),
SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
-SOC_ENUM("DAC Polarity", wm8960_enum[2]),
+SOC_ENUM("DAC Polarity", wm8960_enum[1]),
SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
wm8960_get_deemph, wm8960_put_deemph),
@@ -445,7 +447,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
{
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
struct wm8960_data *pdata = &wm8960->pdata;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct snd_soc_dapm_widget *w;
snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
@@ -476,7 +478,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
* and save the result.
*/
list_for_each_entry(w, &codec->component.card->widgets, list) {
- if (w->dapm != &codec->dapm)
+ if (w->dapm != dapm)
continue;
if (strcmp(w->name, "LOUT1 PGA") == 0)
wm8960->lout1 = w;
@@ -563,6 +565,72 @@ static struct {
{ 8000, 5 },
};
+/* Multiply 256 for internal 256 div */
+static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
+
+/* Multiply 10 to eliminate decimials */
+static const int bclk_divs[] = {
+ 10, 15, 20, 30, 40, 55, 60, 80, 110,
+ 120, 160, 220, 240, 320, 320, 320
+};
+
+static void wm8960_configure_clocking(struct snd_soc_codec *codec,
+ bool tx, int lrclk)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
+ u16 iface2 = snd_soc_read(codec, WM8960_IFACE2);
+ u32 sysclk;
+ int i, j;
+
+ if (!(iface1 & (1<<6))) {
+ dev_dbg(codec->dev,
+ "Codec is slave mode, no need to configure clock\n");
+ return;
+ }
+
+ if (!wm8960->sysclk) {
+ dev_dbg(codec->dev, "No SYSCLK configured\n");
+ return;
+ }
+
+ if (!wm8960->bclk || !lrclk) {
+ dev_dbg(codec->dev, "No audio clocks configured\n");
+ return;
+ }
+
+ 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)
+ break;
+ }
+ if(j != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(dac_divs)) {
+ dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
+ return;
+ }
+
+ /*
+ * 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 bit clock */
+ snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j);
+}
+
static int wm8960_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -570,8 +638,13 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int i;
+ wm8960->bclk = snd_soc_params_to_bclk(params);
+ if (params_channels(params) == 1)
+ wm8960->bclk *= 2;
+
/* bit size */
switch (params_width(params)) {
case 16:
@@ -582,6 +655,12 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
case 24:
iface |= 0x0008;
break;
+ case 32:
+ /* right justify mode does not support 32 word length */
+ if ((iface & 0x3) != 0) {
+ iface |= 0x000c;
+ break;
+ }
default:
dev_err(codec->dev, "unsupported width %d\n",
params_width(params));
@@ -602,6 +681,9 @@ 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));
+
return 0;
}
@@ -627,7 +709,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_STANDBY:
if (!IS_ERR(wm8960->mclk)) {
ret = clk_prepare_enable(wm8960->mclk);
@@ -655,7 +737,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
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) {
regcache_sync(wm8960->regmap);
/* Enable anti-pop features */
@@ -691,8 +773,6 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -707,7 +787,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_STANDBY:
/* Enable anti pop mode */
snd_soc_update_bits(codec, WM8960_APOP1,
@@ -778,7 +858,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_codec_get_bias_level(codec)) {
case SND_SOC_BIAS_PREPARE:
/* Disable HP discharge */
snd_soc_update_bits(codec, WM8960_APOP2,
@@ -802,8 +882,6 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -950,11 +1028,35 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
return wm8960->set_bias_level(codec, level);
}
+static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8960_SYSCLK_MCLK:
+ snd_soc_update_bits(codec, WM8960_CLOCK1,
+ 0x1, WM8960_SYSCLK_MCLK);
+ break;
+ case WM8960_SYSCLK_PLL:
+ snd_soc_update_bits(codec, WM8960_CLOCK1,
+ 0x1, WM8960_SYSCLK_PLL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8960->sysclk = freq;
+
+ return 0;
+}
+
#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
#define WM8960_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)
static const struct snd_soc_dai_ops wm8960_dai_ops = {
.hw_params = wm8960_hw_params,
@@ -962,6 +1064,7 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = {
.set_fmt = wm8960_set_dai_fmt,
.set_clkdiv = wm8960_set_dai_clkdiv,
.set_pll = wm8960_set_dai_pll,
+ .set_sysclk = wm8960_set_dai_sysclk,
};
static struct snd_soc_dai_driver wm8960_dai = {
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 95e2c1bfc809..a057662632ff 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -758,7 +758,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
/* Enable bias generation */
reg = snd_soc_read(codec, WM8961_ANTI_POP);
reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
@@ -773,7 +773,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
/* VREF off */
reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VREF;
@@ -795,8 +795,6 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 118b0034ba23..c5748fd4f296 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2361,7 +2361,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = &wm8962->pdata;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
snd_soc_add_codec_controls(codec, wm8962_snd_controls,
ARRAY_SIZE(wm8962_snd_controls));
@@ -2446,13 +2446,13 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
* So we here provisionally enable it and then disable it afterward
* if current bias_level hasn't reached SND_SOC_BIAS_ON.
*/
- if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+ if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON)
snd_soc_update_bits(codec, WM8962_CLOCKING2,
WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
- if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+ if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON)
snd_soc_update_bits(codec, WM8962_CLOCKING2,
WM8962_SYSCLK_ENA_MASK, 0);
@@ -2510,9 +2510,6 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
static int wm8962_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- if (level == codec->dapm.bias_level)
- return 0;
-
switch (level) {
case SND_SOC_BIAS_ON:
break;
@@ -2530,7 +2527,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x100);
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
msleep(100);
break;
@@ -2538,7 +2535,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -2614,7 +2610,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n",
wm8962->bclk, wm8962->lrclk);
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
wm8962_configure_bclk(codec);
return 0;
@@ -3118,7 +3114,7 @@ static irqreturn_t wm8962_irq(int irq, void *data)
int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int irq_mask, enable;
wm8962->jack = jack;
@@ -3164,7 +3160,7 @@ static void wm8962_beep_work(struct work_struct *work)
struct wm8962_priv *wm8962 =
container_of(work, struct wm8962_priv, beep_work);
struct snd_soc_codec *codec = wm8962->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int i;
int reg = 0;
int best = 0;
@@ -3415,6 +3411,7 @@ static void wm8962_free_gpio(struct snd_soc_codec *codec)
static int wm8962_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int i;
@@ -3462,7 +3459,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
}
if (!dmicclk || !dmicdat) {
dev_dbg(codec->dev, "DMIC not in use, disabling\n");
- snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+ snd_soc_dapm_nc_pin(dapm, "DMICDAT");
}
if (dmicclk != dmicdat)
dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index f9cbabdc6238..b51184c4e816 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -577,7 +577,7 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
flush_delayed_work(&wm8971->charge_work);
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) {
snd_soc_cache_sync(codec);
/* charge output caps - set vmid to 5k for quick power up */
snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
@@ -594,7 +594,6 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8971_PWR1, 0x0001);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index ff0e4646b934..33b16a7ba82e 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -514,7 +514,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
regcache_sync(dev_get_regmap(codec->dev, NULL));
/* Initial cap charge at VMID 5k */
@@ -533,7 +533,6 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index cf7032911721..cfc8cdf49970 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -868,7 +868,7 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec,
/* bit 3: enable bias, bit 2: enable I/O tie off buffer */
power1 |= 0xc;
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
/* Initial cap charge at VMID 5k */
snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1,
power1 | 0x3);
@@ -888,7 +888,6 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec,
dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1);
- codec->dapm.bias_level = level;
return 0;
}
@@ -928,7 +927,7 @@ static int wm8978_suspend(struct snd_soc_codec *codec)
{
struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
- wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
/* Also switch PLL off */
snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
@@ -944,7 +943,7 @@ static int wm8978_resume(struct snd_soc_codec *codec)
/* Sync reg_cache with the hardware */
regcache_sync(wm8978->regmap);
- wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (wm8978->f_pllout)
/* Switch PLL on */
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index 5d1cf08a72b8..2fdd2c6cc09d 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -915,7 +915,7 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
1 << WM8983_VMIDSEL_SHIFT);
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) {
ret = regcache_sync(wm8983->regmap);
if (ret < 0) {
dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
@@ -963,7 +963,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 0b3b54c9971d..8a85f5004d41 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -897,7 +897,7 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
1 << WM8985_VMIDSEL_SHIFT);
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
wm8985->supplies);
if (ret) {
@@ -957,7 +957,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 24968aa8618a..f13a995af277 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -738,7 +738,7 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
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) {
regcache_sync(wm8988->regmap);
/* VREF, VMID=2x5k */
@@ -756,7 +756,6 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8988_PWR1, 0x0000);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index c93bffcb3cfb..1993fd2a6f15 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1124,7 +1124,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regcache_sync(wm8990->regmap);
if (ret < 0) {
dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
@@ -1227,7 +1227,6 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1281,7 +1280,7 @@ static int wm8990_probe(struct snd_soc_codec *codec)
wm8990_reset(codec);
/* charge output caps */
- wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_update_bits(codec, WM8990_AUDIO_INTERFACE_4,
WM8990_ALRCGPIO1, WM8990_ALRCGPIO1);
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 49df0dc607e6..44a677720828 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1131,7 +1131,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
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) {
regcache_sync(wm8991->regmap);
/* Enable all output discharge bits */
snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
@@ -1224,7 +1224,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 2e70a270eb28..8a8db8605dc2 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -992,7 +992,7 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
wm8993->supplies);
if (ret != 0)
@@ -1065,8 +1065,6 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1485,7 +1483,7 @@ static struct snd_soc_dai_driver wm8993_dai = {
static int wm8993_probe(struct snd_soc_codec *codec)
{
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
wm8993->hubs_data.hp_startup_mode = 1;
wm8993->hubs_data.dcs_codes_l = -2;
@@ -1539,7 +1537,7 @@ static int wm8993_probe(struct snd_soc_codec *codec)
* VMID as an output and can disable it.
*/
if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff)
- codec->dapm.idle_bias_off = 1;
+ dapm->idle_bias_off = 1;
return 0;
@@ -1563,7 +1561,7 @@ static int wm8993_suspend(struct snd_soc_codec *codec)
wm8993->fll_fout = fll_fout;
wm8993->fll_fref = fll_fref;
- wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1573,7 +1571,7 @@ static int wm8993_resume(struct snd_soc_codec *codec)
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret;
- wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Restart the FLL? */
if (wm8993->fll_fout) {
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index a1c04dab6684..962e1d31a629 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -212,6 +212,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
static int configure_clock(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
int change, new;
@@ -239,7 +240,7 @@ static int configure_clock(struct snd_soc_codec *codec)
change = snd_soc_update_bits(codec, WM8994_CLOCKING_1,
WM8994_SYSCLK_SRC, new);
if (change)
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
wm8958_micd_set_rate(codec);
@@ -2492,12 +2493,12 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
active_reference(codec);
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) {
switch (control->type) {
case WM8958:
if (control->revision == 0) {
@@ -2521,7 +2522,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
WM8994_LINEOUT2_DISCH);
}
- if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE)
active_dereference(codec);
/* MICBIAS into bypass mode on newer devices */
@@ -2541,20 +2542,18 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
wm8994->cur_fw = NULL;
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
switch (mode) {
case WM8994_VMID_NORMAL:
@@ -3163,7 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
i + 1, ret);
}
- wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -3356,6 +3355,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
int micbias)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994_micdet *micdet;
struct wm8994 *control = wm8994->wm8994;
@@ -3370,20 +3370,16 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
case 1:
micdet = &wm8994->micdet[0];
if (jack)
- ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
- "MICBIAS1");
+ ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
else
- ret = snd_soc_dapm_disable_pin(&codec->dapm,
- "MICBIAS1");
+ ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
break;
case 2:
micdet = &wm8994->micdet[1];
if (jack)
- ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
- "MICBIAS1");
+ ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
else
- ret = snd_soc_dapm_disable_pin(&codec->dapm,
- "MICBIAS1");
+ ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
break;
default:
dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias);
@@ -3415,7 +3411,7 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK,
WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB);
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
return 0;
}
@@ -3505,6 +3501,7 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
/* Should be called with accdet_lock held */
static void wm1811_micd_stop(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
if (!wm8994->jackdet)
@@ -3515,8 +3512,7 @@ static void wm1811_micd_stop(struct snd_soc_codec *codec)
wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
if (wm8994->wm8994->pdata.jd_ext_cap)
- snd_soc_dapm_disable_pin(&codec->dapm,
- "MICBIAS2");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
}
static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
@@ -3625,14 +3621,14 @@ static void wm1811_mic_work(struct work_struct *work)
mic_work.work);
struct wm8994 *control = wm8994->wm8994;
struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
pm_runtime_get_sync(codec->dev);
/* If required for an external cap force MICBIAS on */
if (control->pdata.jd_ext_cap) {
- snd_soc_dapm_force_enable_pin(&codec->dapm,
- "MICBIAS2");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2");
+ snd_soc_dapm_sync(dapm);
}
mutex_lock(&wm8994->accdet_lock);
@@ -3664,6 +3660,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
struct wm8994_priv *wm8994 = data;
struct wm8994 *control = wm8994->wm8994;
struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int reg, delay;
bool present;
@@ -3724,7 +3721,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
/* Turn off MICBIAS if it was on for an external cap */
if (control->pdata.jd_ext_cap && !present)
- snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
if (present)
snd_soc_jack_report(wm8994->micdet[0].jack,
@@ -3770,6 +3767,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
wm1811_micdet_cb det_cb, void *det_cb_data,
wm1811_mic_id_cb id_cb, void *id_cb_data)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
u16 micd_lvl_sel;
@@ -3783,8 +3781,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
if (jack) {
- snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_force_enable_pin(dapm, "CLK_SYS");
+ snd_soc_dapm_sync(dapm);
wm8994->micdet[0].jack = jack;
@@ -3819,7 +3817,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
snd_soc_update_bits(codec, WM8958_MIC_DETECT_2,
WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel);
- WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY);
+ WARN_ON(snd_soc_codec_get_bias_level(codec) > SND_SOC_BIAS_STANDBY);
/*
* If we can use jack detection start off with that,
@@ -3846,8 +3844,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, 0);
wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE);
- snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_disable_pin(dapm, "CLK_SYS");
+ snd_soc_dapm_sync(dapm);
}
return 0;
@@ -3985,9 +3983,9 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data)
static int wm8994_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8994 *control = dev_get_drvdata(codec->dev->parent);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
unsigned int reg;
int ret, i;
@@ -4018,7 +4016,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->micdet_irq = control->pdata.micdet_irq;
/* By default use idle_bias_off, will override for WM8994 */
- codec->dapm.idle_bias_off = 1;
+ dapm->idle_bias_off = 1;
/* Set revision-specific configuration */
switch (control->type) {
@@ -4026,7 +4024,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
/* Single ended line outputs should have VMID on. */
if (!control->pdata.lineout1_diff ||
!control->pdata.lineout2_diff)
- codec->dapm.idle_bias_off = 0;
+ dapm->idle_bias_off = 0;
switch (control->revision) {
case 2:
@@ -4086,7 +4084,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
if (wm8994->micdet_irq)
ret = request_threaded_irq(wm8994->micdet_irq, NULL,
wm8994_mic_irq,
- IRQF_TRIGGER_RISING,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
"Mic1 detect",
wm8994);
else
@@ -4134,7 +4133,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
if (wm8994->micdet_irq) {
ret = request_threaded_irq(wm8994->micdet_irq, NULL,
wm8958_mic_irq,
- IRQF_TRIGGER_RISING,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
"Mic detect",
wm8994);
if (ret != 0)
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 66103c2b012e..505b65f5734f 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -721,6 +721,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
static int configure_clock(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8995_priv *wm8995;
int change, new;
@@ -751,7 +752,7 @@ static int configure_clock(struct snd_soc_codec *codec)
if (!change)
return 0;
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
return 0;
}
@@ -1929,7 +1930,7 @@ static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai,
dai->id + 1, freq);
break;
case WM8995_SYSCLK_MCLK2:
- wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1;
+ wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK2;
wm8995->mclk[1] = freq;
dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
dai->id + 1, freq);
@@ -1965,7 +1966,7 @@ static int wm8995_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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret)
@@ -1990,7 +1991,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 308748a022c5..3dd063f682b2 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -1590,7 +1590,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
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) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
wm8996->supplies);
if (ret != 0) {
@@ -1628,8 +1628,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -2247,7 +2245,7 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
wm8996_polarity_fn polarity_cb)
{
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
wm8996->jack = jack;
wm8996->detecting = true;
@@ -2292,6 +2290,7 @@ EXPORT_SYMBOL_GPL(wm8996_detect);
static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
int val, reg, report;
@@ -2345,12 +2344,14 @@ out:
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
WM8996_MICD_ENA);
- snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_disable_pin(dapm, "Bandgap");
+ snd_soc_dapm_sync(dapm);
}
static void wm8996_hpdet_start(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
/* Unclamp the output, we can't measure while we're shorting it */
snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
WM8996_HPOUT1L_RMV_SHORT |
@@ -2359,8 +2360,8 @@ static void wm8996_hpdet_start(struct snd_soc_codec *codec)
WM8996_HPOUT1R_RMV_SHORT);
/* We need bandgap for HPDET */
- snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_force_enable_pin(dapm, "Bandgap");
+ snd_soc_dapm_sync(dapm);
/* Go into headphone detect left mode */
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
@@ -2646,10 +2647,12 @@ 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, "wm8996", codec);
+ irq_flags | IRQF_ONESHOT,
+ "wm8996", codec);
else
ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq,
- irq_flags, "wm8996", codec);
+ irq_flags | IRQF_ONESHOT,
+ "wm8996", codec);
if (ret == 0) {
/* Unmask the interrupt */
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index a4d11770630c..4134dc7e1243 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -40,7 +40,7 @@ struct wm8997_priv {
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);
-static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
static const struct reg_default wm8997_sysclk_reva_patch[] = {
@@ -106,11 +106,13 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
regmap_write_async(regmap, patch[i].reg,
patch[i].def);
break;
- default:
+ case SND_SOC_DAPM_PRE_PMD:
break;
+ default:
+ return 0;
}
- return 0;
+ return arizona_dvfs_sysclk_ev(w, kcontrol, event);
}
static const char *wm8997_osr_text[] = {
@@ -409,7 +411,8 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
- 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+ 0, wm8997_sysclk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
@@ -1055,13 +1058,14 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
static int wm8997_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
arizona_init_spk(codec);
- snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
- priv->core.arizona->dapm = &codec->dapm;
+ priv->core.arizona->dapm = dapm;
return 0;
}
@@ -1126,6 +1130,8 @@ static int wm8997_probe(struct platform_device *pdev)
wm8997->core.arizona = arizona;
wm8997->core.num_inputs = 4;
+ arizona_init_dvfs(&wm8997->core);
+
for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
wm8997->fll[i].vco_mult = 1;
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 13a3f335ea5b..8a8b1c0f9142 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -838,7 +838,7 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
/* Initial cold start */
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
regcache_cache_only(wm9081->regmap, false);
regcache_sync(wm9081->regmap);
@@ -898,8 +898,6 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index 60d243c904f5..13d23fc797db 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -425,7 +425,7 @@ static const struct snd_soc_dapm_route audio_map_in2_diff[] = {
static int wm9090_add_controls(struct snd_soc_codec *codec)
{
struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int i;
snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets,
@@ -496,7 +496,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
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) {
/* Restore the register cache */
regcache_sync(wm9090->regmap);
}
@@ -515,8 +515,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 98c9525bd751..1fda104dfc45 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -610,7 +610,6 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -646,7 +645,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret == 0) {
/* Sync reg_cache with the hardware after cold reset */
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 79552953e1bd..89cd2d6f57c0 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1054,8 +1054,8 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_48000)
#define WM9713_PCM_FORMATS \
- (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
- SNDRV_PCM_FORMAT_S24_LE)
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
.prepare = ac97_hifi_prepare,
@@ -1171,7 +1171,6 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec,
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1201,7 +1200,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* do we need to re-start the PLL ? */
if (wm9713->pll_in)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index d01c2095452f..0bb415a28723 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -121,6 +122,11 @@
#define ADSP2_WDMA_CONFIG_2 0x31
#define ADSP2_RDMA_CONFIG_1 0x34
+#define ADSP2_SCRATCH0 0x40
+#define ADSP2_SCRATCH1 0x41
+#define ADSP2_SCRATCH2 0x42
+#define ADSP2_SCRATCH3 0x43
+
/*
* ADSP2 Control
*/
@@ -229,26 +235,197 @@ struct wm_coeff_ctl_ops {
struct wm_coeff_ctl {
const char *name;
- struct wm_adsp_alg_region region;
+ const char *fw_name;
+ struct wm_adsp_alg_region alg_region;
struct wm_coeff_ctl_ops ops;
- struct wm_adsp *adsp;
- void *private;
+ struct wm_adsp *dsp;
unsigned int enabled:1;
struct list_head list;
void *cache;
+ unsigned int offset;
size_t len;
unsigned int set:1;
struct snd_kcontrol *kcontrol;
+ unsigned int flags;
};
+#ifdef CONFIG_DEBUG_FS
+static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
+{
+ char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
+
+ mutex_lock(&dsp->debugfs_lock);
+ kfree(dsp->wmfw_file_name);
+ dsp->wmfw_file_name = tmp;
+ mutex_unlock(&dsp->debugfs_lock);
+}
+
+static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
+{
+ char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
+
+ mutex_lock(&dsp->debugfs_lock);
+ kfree(dsp->bin_file_name);
+ dsp->bin_file_name = tmp;
+ mutex_unlock(&dsp->debugfs_lock);
+}
+
+static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
+{
+ mutex_lock(&dsp->debugfs_lock);
+ kfree(dsp->wmfw_file_name);
+ kfree(dsp->bin_file_name);
+ dsp->wmfw_file_name = NULL;
+ dsp->bin_file_name = NULL;
+ mutex_unlock(&dsp->debugfs_lock);
+}
+
+static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wm_adsp *dsp = file->private_data;
+ ssize_t ret;
+
+ mutex_lock(&dsp->debugfs_lock);
+
+ if (!dsp->wmfw_file_name || !dsp->running)
+ ret = 0;
+ else
+ ret = simple_read_from_buffer(user_buf, count, ppos,
+ dsp->wmfw_file_name,
+ strlen(dsp->wmfw_file_name));
+
+ mutex_unlock(&dsp->debugfs_lock);
+ return ret;
+}
+
+static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wm_adsp *dsp = file->private_data;
+ ssize_t ret;
+
+ mutex_lock(&dsp->debugfs_lock);
+
+ if (!dsp->bin_file_name || !dsp->running)
+ ret = 0;
+ else
+ ret = simple_read_from_buffer(user_buf, count, ppos,
+ dsp->bin_file_name,
+ strlen(dsp->bin_file_name));
+
+ mutex_unlock(&dsp->debugfs_lock);
+ return ret;
+}
+
+static const struct {
+ const char *name;
+ const struct file_operations fops;
+} wm_adsp_debugfs_fops[] = {
+ {
+ .name = "wmfw_file_name",
+ .fops = {
+ .open = simple_open,
+ .read = wm_adsp_debugfs_wmfw_read,
+ },
+ },
+ {
+ .name = "bin_file_name",
+ .fops = {
+ .open = simple_open,
+ .read = wm_adsp_debugfs_bin_read,
+ },
+ },
+};
+
+static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
+ struct snd_soc_codec *codec)
+{
+ struct dentry *root = NULL;
+ char *root_name;
+ int i;
+
+ if (!codec->component.debugfs_root) {
+ adsp_err(dsp, "No codec debugfs root\n");
+ goto err;
+ }
+
+ root_name = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!root_name)
+ goto err;
+
+ snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num);
+ root = debugfs_create_dir(root_name, codec->component.debugfs_root);
+ kfree(root_name);
+
+ if (!root)
+ goto err;
+
+ if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
+ goto err;
+
+ if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
+ goto err;
+
+ if (!debugfs_create_x32("fw_version", S_IRUGO, root,
+ &dsp->fw_id_version))
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
+ if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
+ S_IRUGO, root, dsp,
+ &wm_adsp_debugfs_fops[i].fops))
+ goto err;
+ }
+
+ dsp->debugfs_root = root;
+ return;
+
+err:
+ debugfs_remove_recursive(root);
+ adsp_err(dsp, "Failed to create debugfs\n");
+}
+
+static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
+{
+ wm_adsp_debugfs_clear(dsp);
+ debugfs_remove_recursive(dsp->debugfs_root);
+}
+#else
+static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
+ struct snd_soc_codec *codec)
+{
+}
+
+static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
+{
+}
+
+static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
+ const char *s)
+{
+}
+
+static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
+ const char *s)
+{
+}
+
+static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
+{
+}
+#endif
+
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+ ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
return 0;
}
@@ -258,18 +435,18 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
- if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+ if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
return 0;
if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
return -EINVAL;
- if (adsp[e->shift_l].running)
+ if (dsp[e->shift_l].running)
return -EBUSY;
- adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+ dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
return 0;
}
@@ -281,52 +458,17 @@ static const struct soc_enum wm_adsp_fw_enum[] = {
SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
};
-const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
- SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
- wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
- wm_adsp_fw_get, wm_adsp_fw_put),
-};
-EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
-
-#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
-static const struct soc_enum wm_adsp2_rate_enum[] = {
- SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
- ARIZONA_DSP1_RATE_SHIFT, 0xf,
- ARIZONA_RATE_ENUM_SIZE,
- arizona_rate_text, arizona_rate_val),
- SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
- ARIZONA_DSP1_RATE_SHIFT, 0xf,
- ARIZONA_RATE_ENUM_SIZE,
- arizona_rate_text, arizona_rate_val),
- SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
- ARIZONA_DSP1_RATE_SHIFT, 0xf,
- ARIZONA_RATE_ENUM_SIZE,
- arizona_rate_text, arizona_rate_val),
- SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
- ARIZONA_DSP1_RATE_SHIFT, 0xf,
- ARIZONA_RATE_ENUM_SIZE,
- arizona_rate_text, arizona_rate_val),
-};
-
-const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
+const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
wm_adsp_fw_get, wm_adsp_fw_put),
- SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
};
-EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
-#endif
+EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
int type)
@@ -340,28 +482,47 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
return NULL;
}
-static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
unsigned int offset)
{
- if (WARN_ON(!region))
+ if (WARN_ON(!mem))
return offset;
- switch (region->type) {
+ switch (mem->type) {
case WMFW_ADSP1_PM:
- return region->base + (offset * 3);
+ return mem->base + (offset * 3);
case WMFW_ADSP1_DM:
- return region->base + (offset * 2);
+ return mem->base + (offset * 2);
case WMFW_ADSP2_XM:
- return region->base + (offset * 2);
+ return mem->base + (offset * 2);
case WMFW_ADSP2_YM:
- return region->base + (offset * 2);
+ return mem->base + (offset * 2);
case WMFW_ADSP1_ZM:
- return region->base + (offset * 2);
+ return mem->base + (offset * 2);
default:
WARN(1, "Unknown memory region type");
return offset;
}
}
+static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
+{
+ u16 scratch[4];
+ int ret;
+
+ ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
+ scratch, sizeof(scratch));
+ if (ret) {
+ adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+ return;
+ }
+
+ adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
+ be16_to_cpu(scratch[0]),
+ be16_to_cpu(scratch[1]),
+ be16_to_cpu(scratch[2]),
+ be16_to_cpu(scratch[3]));
+}
+
static int wm_coeff_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -372,40 +533,39 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol,
return 0;
}
-static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
const void *buf, size_t len)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
- struct wm_adsp_alg_region *region = &ctl->region;
+ struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
const struct wm_adsp_region *mem;
- struct wm_adsp *adsp = ctl->adsp;
+ struct wm_adsp *dsp = ctl->dsp;
void *scratch;
int ret;
unsigned int reg;
- mem = wm_adsp_find_region(adsp, region->type);
+ mem = wm_adsp_find_region(dsp, alg_region->type);
if (!mem) {
- adsp_err(adsp, "No base for region %x\n",
- region->type);
+ adsp_err(dsp, "No base for region %x\n",
+ alg_region->type);
return -EINVAL;
}
- reg = ctl->region.base;
+ reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg);
scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
- ret = regmap_raw_write(adsp->regmap, reg, scratch,
+ ret = regmap_raw_write(dsp->regmap, reg, scratch,
ctl->len);
if (ret) {
- adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n",
+ adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
ctl->len, reg, ret);
kfree(scratch);
return ret;
}
- adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
+ adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
kfree(scratch);
@@ -424,42 +584,41 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol,
if (!ctl->enabled)
return 0;
- return wm_coeff_write_control(kcontrol, p, ctl->len);
+ return wm_coeff_write_control(ctl, p, ctl->len);
}
-static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
void *buf, size_t len)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
- struct wm_adsp_alg_region *region = &ctl->region;
+ struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
const struct wm_adsp_region *mem;
- struct wm_adsp *adsp = ctl->adsp;
+ struct wm_adsp *dsp = ctl->dsp;
void *scratch;
int ret;
unsigned int reg;
- mem = wm_adsp_find_region(adsp, region->type);
+ mem = wm_adsp_find_region(dsp, alg_region->type);
if (!mem) {
- adsp_err(adsp, "No base for region %x\n",
- region->type);
+ adsp_err(dsp, "No base for region %x\n",
+ alg_region->type);
return -EINVAL;
}
- reg = ctl->region.base;
+ reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg);
scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
- ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
+ ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
if (ret) {
- adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n",
+ adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
ctl->len, reg, ret);
kfree(scratch);
return ret;
}
- adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg);
+ adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
memcpy(buf, scratch, ctl->len);
kfree(scratch);
@@ -473,17 +632,25 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol,
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
char *p = ucontrol->value.bytes.data;
+ if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
+ if (ctl->enabled)
+ return wm_coeff_read_control(ctl, p, ctl->len);
+ else
+ return -EPERM;
+ }
+
memcpy(p, ctl->cache, ctl->len);
+
return 0;
}
struct wmfw_ctl_work {
- struct wm_adsp *adsp;
+ struct wm_adsp *dsp;
struct wm_coeff_ctl *ctl;
struct work_struct work;
};
-static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
+static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
{
struct snd_kcontrol_new *kcontrol;
int ret;
@@ -502,17 +669,25 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
kcontrol->put = wm_coeff_put;
kcontrol->private_value = (unsigned long)ctl;
- ret = snd_soc_add_card_controls(adsp->card,
+ if (ctl->flags) {
+ if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
+ kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ if (ctl->flags & WMFW_CTL_FLAG_READABLE)
+ kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
+ if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+ kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ }
+
+ ret = snd_soc_add_card_controls(dsp->card,
kcontrol, 1);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
- ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
+ ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
ctl->name);
- list_add(&ctl->list, &adsp->ctl_list);
return 0;
err_kcontrol:
@@ -520,6 +695,358 @@ err_kcontrol:
return ret;
}
+static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
+ if (!ctl->enabled || ctl->set)
+ continue;
+ if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+ continue;
+
+ ret = wm_coeff_read_control(ctl,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_adsp *dsp)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
+ if (!ctl->enabled)
+ continue;
+ if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
+ ret = wm_coeff_write_control(ctl,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+ struct wmfw_ctl_work *ctl_work = container_of(work,
+ struct wmfw_ctl_work,
+ work);
+
+ wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
+ kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct wm_adsp *dsp,
+ const struct wm_adsp_alg_region *alg_region,
+ unsigned int offset, unsigned int len,
+ const char *subname, unsigned int subname_len,
+ unsigned int flags)
+{
+ struct wm_coeff_ctl *ctl;
+ struct wmfw_ctl_work *ctl_work;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char *region_name;
+ int ret;
+
+ if (flags & WMFW_CTL_FLAG_SYS)
+ return 0;
+
+ switch (alg_region->type) {
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ break;
+ case WMFW_ADSP2_XM:
+ region_name = "XM";
+ break;
+ case WMFW_ADSP2_YM:
+ region_name = "YM";
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ break;
+ default:
+ adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
+ return -EINVAL;
+ }
+
+ switch (dsp->fw_ver) {
+ case 0:
+ case 1:
+ snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
+ dsp->num, region_name, alg_region->alg);
+ break;
+ default:
+ ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "DSP%d%c %.12s %x", dsp->num, *region_name,
+ wm_adsp_fw_text[dsp->fw], alg_region->alg);
+
+ /* Truncate the subname from the start if it is too long */
+ if (subname) {
+ int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
+ int skip = 0;
+
+ if (subname_len > avail)
+ skip = subname_len - avail;
+
+ snprintf(name + ret,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
+ subname_len - skip, subname + skip);
+ }
+ break;
+ }
+
+ list_for_each_entry(ctl, &dsp->ctl_list,
+ list) {
+ if (!strcmp(ctl->name, name)) {
+ if (!ctl->enabled)
+ ctl->enabled = 1;
+ return 0;
+ }
+ }
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl)
+ return -ENOMEM;
+ ctl->fw_name = wm_adsp_fw_text[dsp->fw];
+ ctl->alg_region = *alg_region;
+ ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+ if (!ctl->name) {
+ ret = -ENOMEM;
+ goto err_ctl;
+ }
+ ctl->enabled = 1;
+ ctl->set = 0;
+ ctl->ops.xget = wm_coeff_get;
+ ctl->ops.xput = wm_coeff_put;
+ ctl->dsp = dsp;
+
+ ctl->flags = flags;
+ ctl->offset = offset;
+ if (len > 512) {
+ adsp_warn(dsp, "Truncating control %s from %d\n",
+ ctl->name, len);
+ len = 512;
+ }
+ ctl->len = len;
+ ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+ if (!ctl->cache) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+
+ list_add(&ctl->list, &dsp->ctl_list);
+
+ ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+ if (!ctl_work) {
+ ret = -ENOMEM;
+ goto err_ctl_cache;
+ }
+
+ ctl_work->dsp = dsp;
+ ctl_work->ctl = ctl;
+ INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+ schedule_work(&ctl_work->work);
+
+ return 0;
+
+err_ctl_cache:
+ kfree(ctl->cache);
+err_ctl_name:
+ kfree(ctl->name);
+err_ctl:
+ kfree(ctl);
+
+ return ret;
+}
+
+struct wm_coeff_parsed_alg {
+ int id;
+ const u8 *name;
+ int name_len;
+ int ncoeff;
+};
+
+struct wm_coeff_parsed_coeff {
+ int offset;
+ int mem_type;
+ const u8 *name;
+ int name_len;
+ int ctl_type;
+ int flags;
+ int len;
+};
+
+static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
+{
+ int length;
+
+ switch (bytes) {
+ case 1:
+ length = **pos;
+ break;
+ case 2:
+ length = le16_to_cpu(*((__le16 *)*pos));
+ break;
+ default:
+ return 0;
+ }
+
+ if (str)
+ *str = *pos + bytes;
+
+ *pos += ((length + bytes) + 3) & ~0x03;
+
+ return length;
+}
+
+static int wm_coeff_parse_int(int bytes, const u8 **pos)
+{
+ int val = 0;
+
+ switch (bytes) {
+ case 2:
+ val = le16_to_cpu(*((__le16 *)*pos));
+ break;
+ case 4:
+ val = le32_to_cpu(*((__le32 *)*pos));
+ break;
+ default:
+ break;
+ }
+
+ *pos += bytes;
+
+ return val;
+}
+
+static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
+ struct wm_coeff_parsed_alg *blk)
+{
+ const struct wmfw_adsp_alg_data *raw;
+
+ switch (dsp->fw_ver) {
+ case 0:
+ case 1:
+ raw = (const struct wmfw_adsp_alg_data *)*data;
+ *data = raw->data;
+
+ blk->id = le32_to_cpu(raw->id);
+ blk->name = raw->name;
+ blk->name_len = strlen(raw->name);
+ blk->ncoeff = le32_to_cpu(raw->ncoeff);
+ break;
+ default:
+ blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
+ blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
+ &blk->name);
+ wm_coeff_parse_string(sizeof(u16), data, NULL);
+ blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
+ break;
+ }
+
+ adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
+ adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
+ adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
+}
+
+static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
+ struct wm_coeff_parsed_coeff *blk)
+{
+ const struct wmfw_adsp_coeff_data *raw;
+ const u8 *tmp;
+ int length;
+
+ switch (dsp->fw_ver) {
+ case 0:
+ case 1:
+ raw = (const struct wmfw_adsp_coeff_data *)*data;
+ *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
+
+ blk->offset = le16_to_cpu(raw->hdr.offset);
+ blk->mem_type = le16_to_cpu(raw->hdr.type);
+ blk->name = raw->name;
+ blk->name_len = strlen(raw->name);
+ blk->ctl_type = le16_to_cpu(raw->ctl_type);
+ blk->flags = le16_to_cpu(raw->flags);
+ blk->len = le32_to_cpu(raw->len);
+ break;
+ default:
+ tmp = *data;
+ blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
+ blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
+ length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
+ blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
+ &blk->name);
+ wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
+ wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
+ blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
+ blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
+ blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
+
+ *data = *data + sizeof(raw->hdr) + length;
+ break;
+ }
+
+ adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
+ adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
+ adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
+ adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
+ adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
+ adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
+}
+
+static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
+ const struct wmfw_region *region)
+{
+ struct wm_adsp_alg_region alg_region = {};
+ struct wm_coeff_parsed_alg alg_blk;
+ struct wm_coeff_parsed_coeff coeff_blk;
+ const u8 *data = region->data;
+ int i, ret;
+
+ wm_coeff_parse_alg(dsp, &data, &alg_blk);
+ for (i = 0; i < alg_blk.ncoeff; i++) {
+ wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
+
+ switch (coeff_blk.ctl_type) {
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ break;
+ default:
+ adsp_err(dsp, "Unknown control type: %d\n",
+ coeff_blk.ctl_type);
+ return -EINVAL;
+ }
+
+ alg_region.type = coeff_blk.mem_type;
+ alg_region.alg = alg_blk.id;
+
+ ret = wm_adsp_create_control(dsp, &alg_region,
+ coeff_blk.offset,
+ coeff_blk.len,
+ coeff_blk.name,
+ coeff_blk.name_len,
+ coeff_blk.flags);
+ if (ret < 0)
+ adsp_err(dsp, "Failed to create control: %.*s, %d\n",
+ coeff_blk.name_len, coeff_blk.name, ret);
+ }
+
+ return 0;
+}
+
static int wm_adsp_load(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@@ -568,12 +1095,22 @@ static int wm_adsp_load(struct wm_adsp *dsp)
goto out_fw;
}
- if (header->ver != 0) {
+ switch (header->ver) {
+ case 0:
+ adsp_warn(dsp, "%s: Depreciated file format %d\n",
+ file, header->ver);
+ break;
+ case 1:
+ case 2:
+ break;
+ default:
adsp_err(dsp, "%s: unknown file format %d\n",
file, header->ver);
goto out_fw;
}
+
adsp_info(dsp, "Firmware version: %d\n", header->ver);
+ dsp->fw_ver = header->ver;
if (header->core != dsp->type) {
adsp_err(dsp, "%s: invalid core %d != %d\n",
@@ -638,6 +1175,12 @@ static int wm_adsp_load(struct wm_adsp *dsp)
text = kzalloc(le32_to_cpu(region->len) + 1,
GFP_KERNEL);
break;
+ case WMFW_ALGORITHM_DATA:
+ region_name = "Algorithm";
+ ret = wm_adsp_parse_coeff(dsp, region);
+ if (ret != 0)
+ goto out_fw;
+ break;
case WMFW_INFO_TEXT:
region_name = "Information";
text = kzalloc(le32_to_cpu(region->len) + 1,
@@ -720,6 +1263,8 @@ static int wm_adsp_load(struct wm_adsp *dsp)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, regions, pos - firmware->size);
+ wm_adsp_debugfs_save_wmfwname(dsp, file);
+
out_fw:
regmap_async_complete(regmap);
wm_adsp_buf_free(&buf_list);
@@ -730,444 +1275,317 @@ out:
return ret;
}
-static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
+static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
+ const struct wm_adsp_alg_region *alg_region)
{
struct wm_coeff_ctl *ctl;
- int ret;
- list_for_each_entry(ctl, &adsp->ctl_list, list) {
- if (!ctl->enabled || ctl->set)
- continue;
- ret = wm_coeff_read_control(ctl->kcontrol,
- ctl->cache,
- ctl->len);
- if (ret < 0)
- return ret;
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
+ if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
+ alg_region->alg == ctl->alg_region.alg &&
+ alg_region->type == ctl->alg_region.type) {
+ ctl->alg_region.base = alg_region->base;
+ }
}
-
- return 0;
}
-static int wm_coeff_sync_controls(struct wm_adsp *adsp)
+static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
+ unsigned int pos, unsigned int len)
{
- struct wm_coeff_ctl *ctl;
+ void *alg;
int ret;
+ __be32 val;
- list_for_each_entry(ctl, &adsp->ctl_list, list) {
- if (!ctl->enabled)
- continue;
- if (ctl->set) {
- ret = wm_coeff_write_control(ctl->kcontrol,
- ctl->cache,
- ctl->len);
- if (ret < 0)
- return ret;
- }
+ if (n_algs == 0) {
+ adsp_err(dsp, "No algorithms\n");
+ return ERR_PTR(-EINVAL);
}
- return 0;
-}
-
-static void wm_adsp_ctl_work(struct work_struct *work)
-{
- struct wmfw_ctl_work *ctl_work = container_of(work,
- struct wmfw_ctl_work,
- work);
-
- wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
- kfree(ctl_work);
-}
-
-static int wm_adsp_create_control(struct wm_adsp *dsp,
- const struct wm_adsp_alg_region *region)
-
-{
- struct wm_coeff_ctl *ctl;
- struct wmfw_ctl_work *ctl_work;
- char *name;
- char *region_name;
- int ret;
-
- name = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
+ if (n_algs > 1024) {
+ adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
+ return ERR_PTR(-EINVAL);
+ }
- switch (region->type) {
- case WMFW_ADSP1_PM:
- region_name = "PM";
- break;
- case WMFW_ADSP1_DM:
- region_name = "DM";
- break;
- case WMFW_ADSP2_XM:
- region_name = "XM";
- break;
- case WMFW_ADSP2_YM:
- region_name = "YM";
- break;
- case WMFW_ADSP1_ZM:
- region_name = "ZM";
- break;
- default:
- ret = -EINVAL;
- goto err_name;
+ /* Read the terminator first to validate the length */
+ ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm list end: %d\n",
+ ret);
+ return ERR_PTR(ret);
}
- snprintf(name, PAGE_SIZE, "DSP%d %s %x",
- dsp->num, region_name, region->alg);
+ if (be32_to_cpu(val) != 0xbedead)
+ adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
+ pos + len, be32_to_cpu(val));
- list_for_each_entry(ctl, &dsp->ctl_list,
- list) {
- if (!strcmp(ctl->name, name)) {
- if (!ctl->enabled)
- ctl->enabled = 1;
- goto found;
- }
- }
+ alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
+ if (!alg)
+ return ERR_PTR(-ENOMEM);
- ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
- if (!ctl) {
- ret = -ENOMEM;
- goto err_name;
- }
- ctl->region = *region;
- ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
- if (!ctl->name) {
- ret = -ENOMEM;
- goto err_ctl;
+ ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm list: %d\n",
+ ret);
+ kfree(alg);
+ return ERR_PTR(ret);
}
- ctl->enabled = 1;
- ctl->set = 0;
- ctl->ops.xget = wm_coeff_get;
- ctl->ops.xput = wm_coeff_put;
- ctl->adsp = dsp;
- ctl->len = region->len;
- ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
- if (!ctl->cache) {
- ret = -ENOMEM;
- goto err_ctl_name;
- }
+ return alg;
+}
- ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
- if (!ctl_work) {
- ret = -ENOMEM;
- goto err_ctl_cache;
- }
+static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
+ int type, __be32 id,
+ __be32 base)
+{
+ struct wm_adsp_alg_region *alg_region;
- ctl_work->adsp = dsp;
- ctl_work->ctl = ctl;
- INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
- schedule_work(&ctl_work->work);
+ alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
+ if (!alg_region)
+ return ERR_PTR(-ENOMEM);
-found:
- kfree(name);
+ alg_region->type = type;
+ alg_region->alg = be32_to_cpu(id);
+ alg_region->base = be32_to_cpu(base);
- return 0;
+ list_add_tail(&alg_region->list, &dsp->alg_regions);
-err_ctl_cache:
- kfree(ctl->cache);
-err_ctl_name:
- kfree(ctl->name);
-err_ctl:
- kfree(ctl);
-err_name:
- kfree(name);
- return ret;
+ if (dsp->fw_ver > 0)
+ wm_adsp_ctl_fixup_base(dsp, alg_region);
+
+ return alg_region;
}
-static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
{
- struct regmap *regmap = dsp->regmap;
struct wmfw_adsp1_id_hdr adsp1_id;
- struct wmfw_adsp2_id_hdr adsp2_id;
struct wmfw_adsp1_alg_hdr *adsp1_alg;
- struct wmfw_adsp2_alg_hdr *adsp2_alg;
- void *alg, *buf;
- struct wm_adsp_alg_region *region;
+ struct wm_adsp_alg_region *alg_region;
const struct wm_adsp_region *mem;
- unsigned int pos, term;
- size_t algs, buf_size;
- __be32 val;
+ unsigned int pos, len;
+ size_t n_algs;
int i, ret;
- switch (dsp->type) {
- case WMFW_ADSP1:
- mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
- break;
- case WMFW_ADSP2:
- mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
- break;
- default:
- mem = NULL;
- break;
- }
-
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
if (WARN_ON(!mem))
return -EINVAL;
- switch (dsp->type) {
- case WMFW_ADSP1:
- ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
- sizeof(adsp1_id));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
- }
-
- buf = &adsp1_id;
- buf_size = sizeof(adsp1_id);
-
- algs = be32_to_cpu(adsp1_id.algs);
- dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
- adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- dsp->fw_id,
- (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp1_id.fw.ver) & 0xff,
- algs);
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
- region->type = WMFW_ADSP1_ZM;
- region->alg = be32_to_cpu(adsp1_id.fw.id);
- region->base = be32_to_cpu(adsp1_id.zm);
- list_add_tail(&region->list, &dsp->alg_regions);
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
- region->type = WMFW_ADSP1_DM;
- region->alg = be32_to_cpu(adsp1_id.fw.id);
- region->base = be32_to_cpu(adsp1_id.dm);
- list_add_tail(&region->list, &dsp->alg_regions);
-
- pos = sizeof(adsp1_id) / 2;
- term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
- break;
-
- case WMFW_ADSP2:
- ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
- sizeof(adsp2_id));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
- }
-
- buf = &adsp2_id;
- buf_size = sizeof(adsp2_id);
-
- algs = be32_to_cpu(adsp2_id.algs);
- dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
- adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- dsp->fw_id,
- (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp2_id.fw.ver) & 0xff,
- algs);
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
- region->type = WMFW_ADSP2_XM;
- region->alg = be32_to_cpu(adsp2_id.fw.id);
- region->base = be32_to_cpu(adsp2_id.xm);
- list_add_tail(&region->list, &dsp->alg_regions);
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
- region->type = WMFW_ADSP2_YM;
- region->alg = be32_to_cpu(adsp2_id.fw.id);
- region->base = be32_to_cpu(adsp2_id.ym);
- list_add_tail(&region->list, &dsp->alg_regions);
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
- region->type = WMFW_ADSP2_ZM;
- region->alg = be32_to_cpu(adsp2_id.fw.id);
- region->base = be32_to_cpu(adsp2_id.zm);
- list_add_tail(&region->list, &dsp->alg_regions);
-
- pos = sizeof(adsp2_id) / 2;
- term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
- break;
-
- default:
- WARN(1, "Unknown DSP type");
- return -EINVAL;
- }
-
- if (algs == 0) {
- adsp_err(dsp, "No algorithms\n");
- return -EINVAL;
- }
-
- if (algs > 1024) {
- adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
- print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
- buf, buf_size);
- return -EINVAL;
- }
-
- /* Read the terminator first to validate the length */
- ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
+ ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
+ sizeof(adsp1_id));
if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list end: %d\n",
- ret);
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
return ret;
}
- if (be32_to_cpu(val) != 0xbedead)
- adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
- term, be32_to_cpu(val));
-
- alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
- if (!alg)
- return -ENOMEM;
-
- ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list: %d\n",
- ret);
- goto out;
- }
-
- adsp1_alg = alg;
- adsp2_alg = alg;
-
- for (i = 0; i < algs; i++) {
- switch (dsp->type) {
- case WMFW_ADSP1:
- adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
- i, be32_to_cpu(adsp1_alg[i].alg.id),
- (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
- be32_to_cpu(adsp1_alg[i].dm),
- be32_to_cpu(adsp1_alg[i].zm));
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region) {
- ret = -ENOMEM;
- goto out;
- }
- region->type = WMFW_ADSP1_DM;
- region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
- region->base = be32_to_cpu(adsp1_alg[i].dm);
- region->len = 0;
- list_add_tail(&region->list, &dsp->alg_regions);
- if (i + 1 < algs) {
- region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
- region->len -= be32_to_cpu(adsp1_alg[i].dm);
- region->len *= 4;
- wm_adsp_create_control(dsp, region);
+ n_algs = be32_to_cpu(adsp1_id.n_algs);
+ dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+ dsp->fw_id,
+ (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp1_id.fw.ver) & 0xff,
+ n_algs);
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
+ adsp1_id.fw.id, adsp1_id.zm);
+ if (IS_ERR(alg_region))
+ return PTR_ERR(alg_region);
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
+ adsp1_id.fw.id, adsp1_id.dm);
+ if (IS_ERR(alg_region))
+ return PTR_ERR(alg_region);
+
+ pos = sizeof(adsp1_id) / 2;
+ len = (sizeof(*adsp1_alg) * n_algs) / 2;
+
+ adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+ if (IS_ERR(adsp1_alg))
+ return PTR_ERR(adsp1_alg);
+
+ for (i = 0; i < n_algs; i++) {
+ adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
+ i, be32_to_cpu(adsp1_alg[i].alg.id),
+ (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(adsp1_alg[i].dm),
+ be32_to_cpu(adsp1_alg[i].zm));
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
+ adsp1_alg[i].alg.id,
+ adsp1_alg[i].dm);
+ if (IS_ERR(alg_region)) {
+ ret = PTR_ERR(alg_region);
+ goto out;
+ }
+ if (dsp->fw_ver == 0) {
+ if (i + 1 < n_algs) {
+ len = be32_to_cpu(adsp1_alg[i + 1].dm);
+ len -= be32_to_cpu(adsp1_alg[i].dm);
+ len *= 4;
+ wm_adsp_create_control(dsp, alg_region, 0,
+ len, NULL, 0, 0);
} else {
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
}
+ }
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region) {
- ret = -ENOMEM;
- goto out;
- }
- region->type = WMFW_ADSP1_ZM;
- region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
- region->base = be32_to_cpu(adsp1_alg[i].zm);
- region->len = 0;
- list_add_tail(&region->list, &dsp->alg_regions);
- if (i + 1 < algs) {
- region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
- region->len -= be32_to_cpu(adsp1_alg[i].zm);
- region->len *= 4;
- wm_adsp_create_control(dsp, region);
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
+ adsp1_alg[i].alg.id,
+ adsp1_alg[i].zm);
+ if (IS_ERR(alg_region)) {
+ ret = PTR_ERR(alg_region);
+ goto out;
+ }
+ if (dsp->fw_ver == 0) {
+ if (i + 1 < n_algs) {
+ len = be32_to_cpu(adsp1_alg[i + 1].zm);
+ len -= be32_to_cpu(adsp1_alg[i].zm);
+ len *= 4;
+ wm_adsp_create_control(dsp, alg_region, 0,
+ len, NULL, 0, 0);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
}
- break;
+ }
+ }
- case WMFW_ADSP2:
- adsp_info(dsp,
- "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
- i, be32_to_cpu(adsp2_alg[i].alg.id),
- (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
- be32_to_cpu(adsp2_alg[i].xm),
- be32_to_cpu(adsp2_alg[i].ym),
- be32_to_cpu(adsp2_alg[i].zm));
-
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region) {
- ret = -ENOMEM;
- goto out;
- }
- region->type = WMFW_ADSP2_XM;
- region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
- region->base = be32_to_cpu(adsp2_alg[i].xm);
- region->len = 0;
- list_add_tail(&region->list, &dsp->alg_regions);
- if (i + 1 < algs) {
- region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
- region->len -= be32_to_cpu(adsp2_alg[i].xm);
- region->len *= 4;
- wm_adsp_create_control(dsp, region);
+out:
+ kfree(adsp1_alg);
+ return ret;
+}
+
+static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
+{
+ struct wmfw_adsp2_id_hdr adsp2_id;
+ struct wmfw_adsp2_alg_hdr *adsp2_alg;
+ struct wm_adsp_alg_region *alg_region;
+ const struct wm_adsp_region *mem;
+ unsigned int pos, len;
+ size_t n_algs;
+ int i, ret;
+
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+ if (WARN_ON(!mem))
+ return -EINVAL;
+
+ ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
+ sizeof(adsp2_id));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ n_algs = be32_to_cpu(adsp2_id.n_algs);
+ dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
+ dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+ dsp->fw_id,
+ (dsp->fw_id_version & 0xff0000) >> 16,
+ (dsp->fw_id_version & 0xff00) >> 8,
+ dsp->fw_id_version & 0xff,
+ n_algs);
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
+ adsp2_id.fw.id, adsp2_id.xm);
+ if (IS_ERR(alg_region))
+ return PTR_ERR(alg_region);
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
+ adsp2_id.fw.id, adsp2_id.ym);
+ if (IS_ERR(alg_region))
+ return PTR_ERR(alg_region);
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
+ adsp2_id.fw.id, adsp2_id.zm);
+ if (IS_ERR(alg_region))
+ return PTR_ERR(alg_region);
+
+ pos = sizeof(adsp2_id) / 2;
+ len = (sizeof(*adsp2_alg) * n_algs) / 2;
+
+ adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+ if (IS_ERR(adsp2_alg))
+ return PTR_ERR(adsp2_alg);
+
+ for (i = 0; i < n_algs; i++) {
+ adsp_info(dsp,
+ "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
+ i, be32_to_cpu(adsp2_alg[i].alg.id),
+ (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(adsp2_alg[i].xm),
+ be32_to_cpu(adsp2_alg[i].ym),
+ be32_to_cpu(adsp2_alg[i].zm));
+
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
+ adsp2_alg[i].alg.id,
+ adsp2_alg[i].xm);
+ if (IS_ERR(alg_region)) {
+ ret = PTR_ERR(alg_region);
+ goto out;
+ }
+ if (dsp->fw_ver == 0) {
+ if (i + 1 < n_algs) {
+ len = be32_to_cpu(adsp2_alg[i + 1].xm);
+ len -= be32_to_cpu(adsp2_alg[i].xm);
+ len *= 4;
+ wm_adsp_create_control(dsp, alg_region, 0,
+ len, NULL, 0, 0);
} else {
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
}
+ }
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region) {
- ret = -ENOMEM;
- goto out;
- }
- region->type = WMFW_ADSP2_YM;
- region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
- region->base = be32_to_cpu(adsp2_alg[i].ym);
- region->len = 0;
- list_add_tail(&region->list, &dsp->alg_regions);
- if (i + 1 < algs) {
- region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
- region->len -= be32_to_cpu(adsp2_alg[i].ym);
- region->len *= 4;
- wm_adsp_create_control(dsp, region);
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
+ adsp2_alg[i].alg.id,
+ adsp2_alg[i].ym);
+ if (IS_ERR(alg_region)) {
+ ret = PTR_ERR(alg_region);
+ goto out;
+ }
+ if (dsp->fw_ver == 0) {
+ if (i + 1 < n_algs) {
+ len = be32_to_cpu(adsp2_alg[i + 1].ym);
+ len -= be32_to_cpu(adsp2_alg[i].ym);
+ len *= 4;
+ wm_adsp_create_control(dsp, alg_region, 0,
+ len, NULL, 0, 0);
} else {
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
}
+ }
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region) {
- ret = -ENOMEM;
- goto out;
- }
- region->type = WMFW_ADSP2_ZM;
- region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
- region->base = be32_to_cpu(adsp2_alg[i].zm);
- region->len = 0;
- list_add_tail(&region->list, &dsp->alg_regions);
- if (i + 1 < algs) {
- region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
- region->len -= be32_to_cpu(adsp2_alg[i].zm);
- region->len *= 4;
- wm_adsp_create_control(dsp, region);
+ alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
+ adsp2_alg[i].alg.id,
+ adsp2_alg[i].zm);
+ if (IS_ERR(alg_region)) {
+ ret = PTR_ERR(alg_region);
+ goto out;
+ }
+ if (dsp->fw_ver == 0) {
+ if (i + 1 < n_algs) {
+ len = be32_to_cpu(adsp2_alg[i + 1].zm);
+ len -= be32_to_cpu(adsp2_alg[i].zm);
+ len *= 4;
+ wm_adsp_create_control(dsp, alg_region, 0,
+ len, NULL, 0, 0);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
}
- break;
}
}
out:
- kfree(alg);
+ kfree(adsp2_alg);
return ret;
}
@@ -1345,6 +1763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, blocks, pos - firmware->size);
+ wm_adsp_debugfs_save_binname(dsp, file);
+
out_fw:
regmap_async_complete(regmap);
release_firmware(firmware);
@@ -1354,10 +1774,13 @@ out:
return ret;
}
-int wm_adsp1_init(struct wm_adsp *adsp)
+int wm_adsp1_init(struct wm_adsp *dsp)
{
- INIT_LIST_HEAD(&adsp->alg_regions);
+ INIT_LIST_HEAD(&dsp->alg_regions);
+#ifdef CONFIG_DEBUG_FS
+ mutex_init(&dsp->debugfs_lock);
+#endif
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp1_init);
@@ -1410,7 +1833,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp);
+ ret = wm_adsp1_setup_algs(dsp);
if (ret != 0)
goto err;
@@ -1531,35 +1954,6 @@ static void wm_adsp2_boot_work(struct work_struct *work)
return;
}
- if (dsp->dvfs) {
- ret = regmap_read(dsp->regmap,
- dsp->base + ADSP2_CLOCKING, &val);
- if (ret != 0) {
- adsp_err(dsp, "Failed to read clocking: %d\n", ret);
- return;
- }
-
- if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
- ret = regulator_enable(dsp->dvfs);
- if (ret != 0) {
- adsp_err(dsp,
- "Failed to enable supply: %d\n",
- ret);
- return;
- }
-
- ret = regulator_set_voltage(dsp->dvfs,
- 1800000,
- 1800000);
- if (ret != 0) {
- adsp_err(dsp,
- "Failed to raise supply: %d\n",
- ret);
- return;
- }
- }
- }
-
ret = wm_adsp2_ena(dsp);
if (ret != 0)
return;
@@ -1568,7 +1962,7 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp);
+ ret = wm_adsp2_setup_algs(dsp);
if (ret != 0)
goto err;
@@ -1642,6 +2036,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
+ /* Log firmware state, it can be useful for analysis */
+ wm_adsp2_show_fw_status(dsp);
+
+ wm_adsp_debugfs_clear(dsp);
+
+ dsp->fw_id = 0;
+ dsp->fw_id_version = 0;
dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
@@ -1653,21 +2054,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
- if (dsp->dvfs) {
- ret = regulator_set_voltage(dsp->dvfs, 1200000,
- 1800000);
- if (ret != 0)
- adsp_warn(dsp,
- "Failed to lower supply: %d\n",
- ret);
-
- ret = regulator_disable(dsp->dvfs);
- if (ret != 0)
- adsp_err(dsp,
- "Failed to enable supply: %d\n",
- ret);
- }
-
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
@@ -1694,7 +2080,25 @@ err:
}
EXPORT_SYMBOL_GPL(wm_adsp2_event);
-int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
+{
+ wm_adsp2_init_debugfs(dsp, codec);
+
+ return snd_soc_add_codec_controls(codec,
+ &wm_adsp_fw_controls[dsp->num - 1],
+ 1);
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe);
+
+int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec)
+{
+ wm_adsp2_cleanup_debugfs(dsp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove);
+
+int wm_adsp2_init(struct wm_adsp *dsp)
{
int ret;
@@ -1702,44 +2106,20 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
* Disable the DSP memory by default when in reset for a small
* power saving.
*/
- ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_MEM_ENA, 0);
if (ret != 0) {
- adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
+ adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
return ret;
}
- INIT_LIST_HEAD(&adsp->alg_regions);
- INIT_LIST_HEAD(&adsp->ctl_list);
- INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
-
- if (dvfs) {
- adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
- if (IS_ERR(adsp->dvfs)) {
- ret = PTR_ERR(adsp->dvfs);
- adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);
- return ret;
- }
-
- ret = regulator_enable(adsp->dvfs);
- if (ret != 0) {
- adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);
- return ret;
- }
-
- ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
- if (ret != 0) {
- adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);
- return ret;
- }
-
- ret = regulator_disable(adsp->dvfs);
- if (ret != 0) {
- adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);
- return ret;
- }
- }
+ INIT_LIST_HEAD(&dsp->alg_regions);
+ INIT_LIST_HEAD(&dsp->ctl_list);
+ INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
+#ifdef CONFIG_DEBUG_FS
+ mutex_init(&dsp->debugfs_lock);
+#endif
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index a4f6b64deb61..579a6350fb01 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -18,8 +18,6 @@
#include "wmfw.h"
-struct regulator;
-
struct wm_adsp_region {
int type;
unsigned int base;
@@ -30,7 +28,6 @@ struct wm_adsp_alg_region {
unsigned int alg;
int type;
unsigned int base;
- size_t len;
};
struct wm_adsp {
@@ -49,37 +46,49 @@ struct wm_adsp {
struct list_head alg_regions;
int fw_id;
+ int fw_id_version;
const struct wm_adsp_region *mem;
int num_mems;
int fw;
- bool running;
-
- struct regulator *dvfs;
+ int fw_ver;
+ u32 running;
struct list_head ctl_list;
struct work_struct boot_work;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_root;
+ struct mutex debugfs_lock;
+ char *wmfw_file_name;
+ char *bin_file_name;
+#endif
+
};
#define WM_ADSP1(wname, num) \
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
-#define WM_ADSP2(wname, num) \
+#define WM_ADSP2_E(wname, num, event_fn) \
{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
- .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \
- .event_flags = SND_SOC_DAPM_PRE_PMU }, \
+ .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \
{ .id = snd_soc_dapm_out_drv, .name = wname, \
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
-extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
-extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
+#define WM_ADSP2(wname, num) \
+ WM_ADSP2_E(wname, num, wm_adsp2_early_event)
+
+extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
-int wm_adsp1_init(struct wm_adsp *adsp);
-int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
+int wm_adsp1_init(struct wm_adsp *dsp);
+int wm_adsp2_init(struct wm_adsp *dsp);
+int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec);
+int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 8366e19657a7..fd86bd105460 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -1116,7 +1116,7 @@ static const struct snd_soc_dapm_route lineout2_se_routes[] = {
int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
/* Latch volume update bits & default ZC on */
snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
@@ -1160,7 +1160,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
int lineout1_diff, int lineout2_diff)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
hubs->codec = codec;
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index ef163360a745..7613d60d62ea 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -15,6 +15,17 @@
#include <linux/types.h>
+#define WMFW_MAX_ALG_NAME 256
+#define WMFW_MAX_ALG_DESCR_NAME 256
+
+#define WMFW_MAX_COEFF_NAME 256
+#define WMFW_MAX_COEFF_DESCR_NAME 256
+
+#define WMFW_CTL_FLAG_SYS 0x8000
+#define WMFW_CTL_FLAG_VOLATILE 0x0004
+#define WMFW_CTL_FLAG_WRITEABLE 0x0002
+#define WMFW_CTL_FLAG_READABLE 0x0001
+
struct wmfw_header {
char magic[4];
__le32 len;
@@ -61,7 +72,7 @@ struct wmfw_adsp1_id_hdr {
struct wmfw_id_hdr fw;
__be32 zm;
__be32 dm;
- __be32 algs;
+ __be32 n_algs;
} __packed;
struct wmfw_adsp2_id_hdr {
@@ -69,7 +80,7 @@ struct wmfw_adsp2_id_hdr {
__be32 zm;
__be32 xm;
__be32 ym;
- __be32 algs;
+ __be32 n_algs;
} __packed;
struct wmfw_alg_hdr {
@@ -90,6 +101,28 @@ struct wmfw_adsp2_alg_hdr {
__be32 ym;
} __packed;
+struct wmfw_adsp_alg_data {
+ __le32 id;
+ u8 name[WMFW_MAX_ALG_NAME];
+ u8 descr[WMFW_MAX_ALG_DESCR_NAME];
+ __le32 ncoeff;
+ u8 data[];
+} __packed;
+
+struct wmfw_adsp_coeff_data {
+ struct {
+ __le16 offset;
+ __le16 type;
+ __le32 size;
+ } hdr;
+ u8 name[WMFW_MAX_COEFF_NAME];
+ u8 descr[WMFW_MAX_COEFF_DESCR_NAME];
+ __le16 ctl_type;
+ __le16 flags;
+ __le32 len;
+ u8 data[];
+} __packed;
+
struct wmfw_coeff_hdr {
u8 magic[4];
__le32 len;
@@ -117,9 +150,10 @@ struct wmfw_coeff_item {
#define WMFW_ADSP1 1
#define WMFW_ADSP2 2
-#define WMFW_ABSOLUTE 0xf0
-#define WMFW_NAME_TEXT 0xfe
-#define WMFW_INFO_TEXT 0xff
+#define WMFW_ABSOLUTE 0xf0
+#define WMFW_ALGORITHM_DATA 0xf2
+#define WMFW_NAME_TEXT 0xfe
+#define WMFW_INFO_TEXT 0xff
#define WMFW_ADSP1_PM 2
#define WMFW_ADSP1_DM 3
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 23c91fa65ab8..b960e626dad9 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -107,6 +107,7 @@ struct davinci_mcasp {
#endif
struct davinci_mcasp_ruledata ruledata[2];
+ struct snd_pcm_hw_constraint_list chconstr[2];
};
static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
@@ -685,6 +686,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
if (mcasp->serial_dir[i] == TX_MODE &&
tx_ser < max_active_serializers) {
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ DISMOD_LOW, DISMOD_MASK);
tx_ser++;
} else if (mcasp->serial_dir[i] == RX_MODE &&
rx_ser < max_active_serializers) {
@@ -915,15 +918,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
* the machine driver, we need to calculate the ratio.
*/
if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
- int channels = params_channels(params);
+ int slots = mcasp->tdm_slots;
int rate = params_rate(params);
int sbits = params_width(params);
int ppm, div;
- if (channels > mcasp->tdm_slots)
- channels = mcasp->tdm_slots;
-
- div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*channels,
+ div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
&ppm);
if (ppm)
dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
@@ -1024,31 +1024,36 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval *ri =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
int sbits = params_width(params);
- int channels = params_channels(params);
- unsigned int list[ARRAY_SIZE(davinci_mcasp_dai_rates)];
- int i, count = 0;
+ int slots = rd->mcasp->tdm_slots;
+ struct snd_interval range;
+ int i;
- if (channels > rd->mcasp->tdm_slots)
- channels = rd->mcasp->tdm_slots;
+ snd_interval_any(&range);
+ range.empty = 1;
for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) {
- if (ri->min <= davinci_mcasp_dai_rates[i] &&
- ri->max >= davinci_mcasp_dai_rates[i]) {
- uint bclk_freq = sbits*channels*
+ if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) {
+ uint bclk_freq = sbits*slots*
davinci_mcasp_dai_rates[i];
int ppm;
davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
- if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM)
- list[count++] = davinci_mcasp_dai_rates[i];
+ if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
+ if (range.empty) {
+ range.min = davinci_mcasp_dai_rates[i];
+ range.empty = 0;
+ }
+ range.max = davinci_mcasp_dai_rates[i];
+ }
}
}
+
dev_dbg(rd->mcasp->dev,
- "%d frequencies (%d-%d) for %d sbits and %d channels\n",
- count, ri->min, ri->max, sbits, channels);
+ "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n",
+ ri->min, ri->max, range.min, range.max, sbits, slots);
- return snd_interval_list(hw_param_interval(params, rule->var),
- count, list, 0);
+ return snd_interval_refine(hw_param_interval(params, rule->var),
+ &range);
}
static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
@@ -1058,17 +1063,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_mask nfmt;
int rate = params_rate(params);
- int channels = params_channels(params);
+ int slots = rd->mcasp->tdm_slots;
int i, count = 0;
snd_mask_none(&nfmt);
- if (channels > rd->mcasp->tdm_slots)
- channels = rd->mcasp->tdm_slots;
-
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
if (snd_mask_test(fmt, i)) {
- uint bclk_freq = snd_pcm_format_width(i)*channels*rate;
+ uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
int ppm;
davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
@@ -1079,51 +1081,12 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
}
}
dev_dbg(rd->mcasp->dev,
- "%d possible sample format for %d Hz and %d channels\n",
- count, rate, channels);
+ "%d possible sample format for %d Hz and %d tdm slots\n",
+ count, rate, slots);
return snd_mask_refine(fmt, &nfmt);
}
-static int davinci_mcasp_hw_rule_channels(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct davinci_mcasp_ruledata *rd = rule->private;
- struct snd_interval *ci =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- int sbits = params_width(params);
- int rate = params_rate(params);
- int max_chan_per_wire = rd->mcasp->tdm_slots < ci->max ?
- rd->mcasp->tdm_slots : ci->max;
- unsigned int list[ci->max - ci->min + 1];
- int c1, c, count = 0;
-
- for (c1 = ci->min; c1 <= max_chan_per_wire; c1++) {
- uint bclk_freq = c1*sbits*rate;
- int ppm;
-
- davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
- if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
- /* If we can use all tdm_slots, we can put any
- amount of channels to remaining wires as
- long as they fit in. */
- if (c1 == rd->mcasp->tdm_slots) {
- for (c = c1; c <= rd->serializers*c1 &&
- c <= ci->max; c++)
- list[count++] = c;
- } else {
- list[count++] = c1;
- }
- }
- }
- dev_dbg(rd->mcasp->dev,
- "%d possible channel counts (%d-%d) for %d Hz and %d sbits\n",
- count, ci->min, ci->max, rate, sbits);
-
- return snd_interval_list(hw_param_interval(params, rule->var),
- count, list, 0);
-}
-
static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
@@ -1167,6 +1130,11 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_CHANNELS,
2, max_channels);
+ if (mcasp->chconstr[substream->stream].count)
+ snd_pcm_hw_constraint_list(substream->runtime,
+ 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mcasp->chconstr[substream->stream]);
+
/*
* If we rely on implicit BCLK divider setting we should
* set constraints based on what we can provide.
@@ -1180,24 +1148,14 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_RATE,
davinci_mcasp_hw_rule_rate,
ruledata,
- SNDRV_PCM_HW_PARAM_FORMAT,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (ret)
return ret;
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
davinci_mcasp_hw_rule_format,
ruledata,
- SNDRV_PCM_HW_PARAM_RATE,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- if (ret)
- return ret;
- ret = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- davinci_mcasp_hw_rule_channels,
- ruledata,
- SNDRV_PCM_HW_PARAM_RATE,
- SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ SNDRV_PCM_HW_PARAM_RATE, -1);
if (ret)
return ret;
}
@@ -1556,6 +1514,102 @@ nodata:
return pdata;
}
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
+ struct snd_pcm_hw_constraint_list *cl,
+ int serializers)
+{
+ unsigned int *list;
+ int i, count = 0;
+
+ if (serializers <= 1)
+ return 0;
+
+ list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (mcasp->tdm_slots + serializers - 2),
+ GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+
+ for (i = 2; i <= mcasp->tdm_slots; i++)
+ list[count++] = i;
+
+ for (i = 2; i <= serializers; i++)
+ list[count++] = i*mcasp->tdm_slots;
+
+ cl->count = count;
+ cl->list = list;
+
+ return 0;
+}
+
+
+static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
+{
+ int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+ for (i = 0; i < mcasp->num_serializer; i++)
+ if (mcasp->serial_dir[i] == TX_MODE)
+ tx_serializers++;
+ else if (mcasp->serial_dir[i] == RX_MODE)
+ rx_serializers++;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
+ SNDRV_PCM_STREAM_PLAYBACK],
+ tx_serializers);
+ if (ret)
+ return ret;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
+ SNDRV_PCM_STREAM_CAPTURE],
+ rx_serializers);
+
+ return ret;
+}
+
+enum {
+ PCM_EDMA,
+ PCM_SDMA,
+};
+static const char *sdma_prefix = "ti,omap";
+
+static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
+{
+ struct dma_chan *chan;
+ const char *tmp;
+ int ret = PCM_EDMA;
+
+ if (!mcasp->dev->of_node)
+ return PCM_EDMA;
+
+ tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data;
+ chan = dma_request_slave_channel_reason(mcasp->dev, tmp);
+ if (IS_ERR(chan)) {
+ if (PTR_ERR(chan) != -EPROBE_DEFER)
+ dev_err(mcasp->dev,
+ "Can't verify DMA configuration (%ld)\n",
+ PTR_ERR(chan));
+ return PTR_ERR(chan);
+ }
+ BUG_ON(!chan->device || !chan->device->dev);
+
+ if (chan->device->dev->of_node)
+ ret = of_property_read_string(chan->device->dev->of_node,
+ "compatible", &tmp);
+ else
+ dev_dbg(mcasp->dev, "DMA controller has no of-node\n");
+
+ dma_release_channel(chan);
+ if (ret)
+ return ret;
+
+ dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp);
+ if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix)))
+ return PCM_SDMA;
+
+ return PCM_EDMA;
+}
+
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1739,6 +1793,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
}
+ ret = davinci_mcasp_init_ch_constraints(mcasp);
+ if (ret)
+ goto err;
+
dev_set_drvdata(&pdev->dev, mcasp);
mcasp_reparent_fck(pdev);
@@ -1750,27 +1808,34 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (ret != 0)
goto err;
- switch (mcasp->version) {
+ ret = davinci_mcasp_get_dma_type(mcasp);
+ switch (ret) {
+ case PCM_EDMA:
#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \
(IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
IS_MODULE(CONFIG_SND_EDMA_SOC))
- case MCASP_VERSION_1:
- case MCASP_VERSION_2:
- case MCASP_VERSION_3:
ret = edma_pcm_platform_register(&pdev->dev);
- break;
+#else
+ dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
+ ret = -EINVAL;
+ goto err;
#endif
+ break;
+ case PCM_SDMA:
#if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \
(IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
IS_MODULE(CONFIG_SND_OMAP_SOC))
- case MCASP_VERSION_4:
ret = omap_pcm_platform_register(&pdev->dev);
- break;
+#else
+ dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
+ ret = -EINVAL;
+ goto err;
#endif
+ break;
default:
- dev_err(&pdev->dev, "Invalid McASP version: %d\n",
- mcasp->version);
- ret = -EINVAL;
+ dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
+ case -EPROBE_DEFER:
+ goto err;
break;
}
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 79dc511180bf..a3be108a8c17 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -215,7 +215,10 @@
* DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
*/
#define MODE(val) (val)
-#define DISMOD (val)(val<<2)
+#define DISMOD_3STATE (0x0)
+#define DISMOD_LOW (0x2 << 2)
+#define DISMOD_HIGH (0x3 << 2)
+#define DISMOD_MASK DISMOD_HIGH
#define TXSTATE BIT(4)
#define RXSTATE BIT(5)
#define SRMOD_MASK 3
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 93d7e56c6066..ccadefceeff2 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -445,7 +445,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
return ret;
}
- dma->assigned = 1;
+ dma->assigned = true;
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware);
@@ -814,7 +814,7 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
substream->runtime->private_data = NULL;
}
- dma->assigned = 0;
+ dma->assigned = false;
return 0;
}
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index ec79c3d5e65e..5c73bea7b11e 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1,7 +1,7 @@
/*
* Freescale ALSA SoC Digital Audio Interface (SAI) driver.
*
- * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ * Copyright 2012-2015 Freescale Semiconductor, Inc.
*
* 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
@@ -27,6 +27,17 @@
#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
FSL_SAI_CSR_FEIE)
+static u32 fsl_sai_rates[] = {
+ 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000
+};
+
+static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
+ .count = ARRAY_SIZE(fsl_sai_rates),
+ .list = fsl_sai_rates,
+};
+
static irqreturn_t fsl_sai_isr(int irq, void *devid)
{
struct fsl_sai *sai = (struct fsl_sai *)devid;
@@ -251,12 +262,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFM:
+ sai->is_slave_mode = true;
break;
case SND_SOC_DAIFMT_CBS_CFM:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
break;
case SND_SOC_DAIFMT_CBM_CFS:
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
+ sai->is_slave_mode = true;
break;
default:
return -EINVAL;
@@ -288,6 +301,79 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
return ret;
}
+static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long clk_rate;
+ u32 savediv = 0, ratio, savesub = freq;
+ u32 id;
+ int ret = 0;
+
+ /* Don't apply to slave mode */
+ if (sai->is_slave_mode)
+ return 0;
+
+ for (id = 0; id < FSL_SAI_MCLK_MAX; id++) {
+ clk_rate = clk_get_rate(sai->mclk_clk[id]);
+ if (!clk_rate)
+ continue;
+
+ ratio = clk_rate / freq;
+
+ ret = clk_rate - ratio * freq;
+
+ /*
+ * Drop the source that can not be
+ * divided into the required rate.
+ */
+ if (ret != 0 && clk_rate / ret < 1000)
+ continue;
+
+ dev_dbg(dai->dev,
+ "ratio %d for freq %dHz based on clock %ldHz\n",
+ ratio, freq, clk_rate);
+
+ if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512)
+ ratio /= 2;
+ else
+ continue;
+
+ if (ret < savesub) {
+ savediv = ratio;
+ sai->mclk_id[tx] = id;
+ savesub = ret;
+ }
+
+ if (ret == 0)
+ break;
+ }
+
+ if (savediv == 0) {
+ dev_err(dai->dev, "failed to derive required %cx rate: %d\n",
+ tx ? 'T' : 'R', freq);
+ return -EINVAL;
+ }
+
+ if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
+ FSL_SAI_CR2_MSEL_MASK,
+ FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
+ FSL_SAI_CR2_DIV_MASK, savediv - 1);
+ } else {
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
+ FSL_SAI_CR2_MSEL_MASK,
+ FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
+ FSL_SAI_CR2_DIV_MASK, savediv - 1);
+ }
+
+ dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
+ sai->mclk_id[tx], savediv, savesub);
+
+ return 0;
+}
+
static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
@@ -297,6 +383,24 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
unsigned int channels = params_channels(params);
u32 word_width = snd_pcm_format_width(params_format(params));
u32 val_cr4 = 0, val_cr5 = 0;
+ int ret;
+
+ if (!sai->is_slave_mode) {
+ ret = fsl_sai_set_bclk(cpu_dai, tx,
+ 2 * word_width * params_rate(params));
+ if (ret)
+ return ret;
+
+ /* Do not enable the clock if it is already enabled */
+ if (!(sai->mclk_streams & BIT(substream->stream))) {
+ ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]);
+ if (ret)
+ return ret;
+
+ sai->mclk_streams |= BIT(substream->stream);
+ }
+
+ }
if (!sai->is_dsp_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
@@ -322,6 +426,22 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ if (!sai->is_slave_mode &&
+ sai->mclk_streams & BIT(substream->stream)) {
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
+ sai->mclk_streams &= ~BIT(substream->stream);
+ }
+
+ return 0;
+}
+
+
static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
@@ -410,7 +530,10 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
FSL_SAI_CR3_TRCE);
- return 0;
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
+
+ return ret;
}
static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
@@ -428,6 +551,7 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt,
.hw_params = fsl_sai_hw_params,
+ .hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
.startup = fsl_sai_startup,
.shutdown = fsl_sai_shutdown,
@@ -463,14 +587,18 @@ static struct snd_soc_dai_driver fsl_sai_dai = {
.stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_SAI_FORMATS,
},
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_SAI_FORMATS,
},
.ops = &fsl_sai_pcm_dai_ops,
@@ -600,8 +728,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->bus_clk = NULL;
}
- for (i = 0; i < FSL_SAI_MCLK_MAX; i++) {
- sprintf(tmp, "mclk%d", i + 1);
+ sai->mclk_clk[0] = sai->bus_clk;
+ for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
+ sprintf(tmp, "mclk%d", i);
sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
if (IS_ERR(sai->mclk_clk[i])) {
dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
@@ -664,8 +793,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (sai->sai_on_imx)
return imx_pcm_dma_init(pdev);
else
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
- SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
}
static const struct of_device_id fsl_sai_ids[] = {
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 34667209b607..066280953c85 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -72,13 +72,15 @@
/* SAI Transmit and Recieve Configuration 2 Register */
#define FSL_SAI_CR2_SYNC BIT(30)
-#define FSL_SAI_CR2_MSEL_MASK (0xff << 26)
+#define FSL_SAI_CR2_MSEL_MASK (0x3 << 26)
#define FSL_SAI_CR2_MSEL_BUS 0
#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26)
#define FSL_SAI_CR2_MSEL_MCLK2 BIT(27)
#define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27))
+#define FSL_SAI_CR2_MSEL(ID) ((ID) << 26)
#define FSL_SAI_CR2_BCP BIT(25)
#define FSL_SAI_CR2_BCD_MSTR BIT(24)
+#define FSL_SAI_CR2_DIV_MASK 0xff
/* SAI Transmit and Recieve Configuration 3 Register */
#define FSL_SAI_CR3_TRCE BIT(16)
@@ -120,7 +122,7 @@
#define FSL_SAI_CLK_MAST2 2
#define FSL_SAI_CLK_MAST3 3
-#define FSL_SAI_MCLK_MAX 3
+#define FSL_SAI_MCLK_MAX 4
/* SAI data transfer numbers per DMA request */
#define FSL_SAI_MAXBURST_TX 6
@@ -132,11 +134,14 @@ struct fsl_sai {
struct clk *bus_clk;
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
+ bool is_slave_mode;
bool is_lsb_first;
bool is_dsp_mode;
bool sai_on_imx;
bool synchronous[2];
+ unsigned int mclk_id[2];
+ unsigned int mclk_streams;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
};
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 91eb3aef7f02..8e932219cb3a 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -417,11 +417,9 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
if (clk != STC_TXCLK_SPDIF_ROOT)
goto clk_set_bypass;
- /*
- * The S/PDIF block needs a clock of 64 * fs * txclk_df.
- * So request 64 * fs * (txclk_df + 1) to get rounded.
- */
- ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
+ /* The S/PDIF block needs a clock of 64 * fs * txclk_df */
+ ret = clk_set_rate(spdif_priv->txclk[rate],
+ 64 * sample_rate * txclk_df);
if (ret) {
dev_err(&pdev->dev, "failed to set tx clock rate\n");
return ret;
@@ -1060,7 +1058,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
- rate_ideal = rate[index] * (txclk_df + 1) * 64;
+ rate_ideal = rate[index] * txclk_df * 64;
if (round)
rate_actual = clk_round_rate(clk, rate_ideal);
else
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 0d48804218b1..c7647e066cfd 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1292,13 +1292,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
void __iomem *iomem;
char name[64];
- /* SSIs that are not connected on the board should have a
- * status = "disabled"
- * property in their device tree nodes.
- */
- if (!of_device_is_available(np))
- return -ENODEV;
-
of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
if (!of_id || !of_id->data)
return -EINVAL;
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index d9050d946ae7..fc57da341d61 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -184,7 +184,7 @@ static enum imx_audmux_type {
IMX31_AUDMUX,
} audmux_type;
-static struct platform_device_id imx_audmux_ids[] = {
+static const struct platform_device_id imx_audmux_ids[] = {
{
.name = "imx21-audmux",
.driver_data = IMX21_AUDMUX,
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
index 9e6493d4e7ff..bb0459018b45 100644
--- a/sound/soc/fsl/imx-mc13783.c
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -45,11 +45,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
- if (ret)
- return ret;
-
- return 0;
+ return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
}
static struct snd_soc_ops imx_mc13783_hifi_ops = {
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index cd146d4fa805..b38b98cae855 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -190,7 +190,7 @@ static int imx_wm8962_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "audmux internal port setup failed\n");
return ret;
}
- imx_audmux_v2_configure_port(ext_port,
+ ret = imx_audmux_v2_configure_port(ext_port,
IMX_AUDMUX_V2_PTCR_SYN,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 33feee9ca8c3..d5554939146e 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -26,6 +26,7 @@ struct simple_card_data {
struct simple_dai_props {
struct asoc_simple_dai cpu_dai;
struct asoc_simple_dai codec_dai;
+ unsigned int mclk_fs;
} *dai_props;
unsigned int mclk_fs;
int gpio_hp_det;
@@ -76,11 +77,18 @@ 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 simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- unsigned int mclk;
+ struct simple_dai_props *dai_props =
+ &priv->dai_props[rtd - rtd->card->rtd];
+ unsigned int mclk, mclk_fs = 0;
int ret = 0;
- if (priv->mclk_fs) {
- mclk = params_rate(params) * priv->mclk_fs;
+ if (priv->mclk_fs)
+ mclk_fs = priv->mclk_fs;
+ else if (dai_props->mclk_fs)
+ mclk_fs = dai_props->mclk_fs;
+
+ if (mclk_fs) {
+ mclk = params_rate(params) * mclk_fs;
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
}
@@ -307,11 +315,13 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
struct device_node *cpu = NULL;
+ struct device_node *plat = NULL;
struct device_node *codec = NULL;
char *name;
char prop[128];
char *prefix = "";
int ret, cpu_args;
+ u32 val;
/* For single DAI link & old style of DT node */
if (is_top_level_node)
@@ -320,6 +330,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
snprintf(prop, sizeof(prop), "%scpu", prefix);
cpu = of_get_child_by_name(node, prop);
+ snprintf(prop, sizeof(prop), "%splat", prefix);
+ plat = of_get_child_by_name(node, prop);
+
snprintf(prop, sizeof(prop), "%scodec", prefix);
codec = of_get_child_by_name(node, prop);
@@ -334,6 +347,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
if (ret < 0)
goto dai_link_of_err;
+ if (!of_property_read_u32(node, "mclk-fs", &val))
+ dai_props->mclk_fs = val;
+
ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
&dai_link->cpu_of_node,
&dai_link->cpu_dai_name,
@@ -352,8 +368,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
goto dai_link_of_err;
}
- /* Simple Card assumes platform == cpu */
- dai_link->platform_of_node = dai_link->cpu_of_node;
+ if (plat) {
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_args(plat, "sound-dai",
+ "#sound-dai-cells", 0, &args);
+ dai_link->platform_of_node = args.np;
+ } else {
+ /* Assumes platform == cpu */
+ dai_link->platform_of_node = dai_link->cpu_of_node;
+ }
/* DAI link name is created from CPU/CODEC dai name */
name = devm_kzalloc(dev,
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index ee03dbdda235..f3060a4ca040 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -79,7 +79,6 @@ config SND_SOC_INTEL_BROADWELL_MACH
depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \
I2C_DESIGNWARE_PLATFORM
select SND_SOC_INTEL_HASWELL
- select SND_COMPRESS_OFFLOAD
select SND_SOC_RT286
help
This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
@@ -112,12 +111,24 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
If unsure select "N".
config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
- tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645 codec"
- depends on X86_INTEL_LPSS
+ tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
+ depends on X86_INTEL_LPSS && I2C
select SND_SOC_RT5645
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
help
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
- platforms with RT5645 audio codec.
+ platforms with RT5645/5650 audio codec.
If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
+ tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
+ depends on X86_INTEL_LPSS && I2C
+ select SND_SOC_MAX98090
+ select SND_SOC_TS3A227E
+ select SND_SST_MFLD_PLATFORM
+ select SND_SST_IPC_ACPI
+ help
+ 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".
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index 90aa5c0476f3..31e9b9ecbb8a 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -774,8 +774,120 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
return ret;
}
+int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ ctx->ssp_cmd.nb_slots = slots;
+ ctx->ssp_cmd.active_tx_slot_map = tx_mask;
+ ctx->ssp_cmd.active_rx_slot_map = rx_mask;
+ ctx->ssp_cmd.nb_bits_per_slots = slot_width;
+
+ return 0;
+}
+
+static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ int format;
+
+ format = fmt & SND_SOC_DAIFMT_INV_MASK;
+ dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
+
+ switch (format) {
+ case SND_SOC_DAIFMT_NB_NF:
+ return SSP_FS_ACTIVE_LOW;
+ case SND_SOC_DAIFMT_NB_IF:
+ return SSP_FS_ACTIVE_HIGH;
+ case SND_SOC_DAIFMT_IB_IF:
+ return SSP_FS_ACTIVE_LOW;
+ case SND_SOC_DAIFMT_IB_NF:
+ return SSP_FS_ACTIVE_HIGH;
+ default:
+ dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);
+ }
+
+ return -EINVAL;
+}
+
+static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int format;
+
+ format = (fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
+
+ switch (format) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ return SSP_MODE_MASTER;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ return SSP_MODE_SLAVE;
+ default:
+ dev_err(dai->dev, "Invalid ssp protocol: %d\n", format);
+ }
+
+ return -EINVAL;
+}
+
+
+int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int mode;
+ int fs_polarity;
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (mode) {
+ case SND_SOC_DAIFMT_DSP_B:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);
+ ctx->ssp_cmd.start_delay = 0;
+ ctx->ssp_cmd.data_polarity = 1;
+ ctx->ssp_cmd.frame_sync_width = 1;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);
+ ctx->ssp_cmd.start_delay = 1;
+ ctx->ssp_cmd.data_polarity = 1;
+ ctx->ssp_cmd.frame_sync_width = 1;
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);
+ ctx->ssp_cmd.start_delay = 1;
+ ctx->ssp_cmd.data_polarity = 0;
+ ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);
+ ctx->ssp_cmd.start_delay = 0;
+ ctx->ssp_cmd.data_polarity = 0;
+ ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;
+ break;
+
+ default:
+ dev_dbg(dai->dev, "using default ssp configs\n");
+ }
+
+ fs_polarity = sst_get_frame_sync_polarity(dai, fmt);
+ if (fs_polarity < 0)
+ return fs_polarity;
+
+ ctx->ssp_cmd.frame_sync_polarity = fs_polarity;
+
+ return 0;
+}
+
/**
* sst_ssp_config - contains SSP configuration for media UC
+ * this can be overwritten by set_dai_xxx APIs
*/
static const struct sst_ssp_config sst_ssp_configs = {
.ssp_id = SSP_CODEC,
@@ -789,47 +901,56 @@ static const struct sst_ssp_config sst_ssp_configs = {
.fs_frequency = SSP_FS_48_KHZ,
.active_slot_map = 0xF,
.start_delay = 0,
+ .frame_sync_polarity = SSP_FS_ACTIVE_HIGH,
+ .data_polarity = 1,
};
+void sst_fill_ssp_defaults(struct snd_soc_dai *dai)
+{
+ const struct sst_ssp_config *config;
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ config = &sst_ssp_configs;
+
+ ctx->ssp_cmd.selection = config->ssp_id;
+ ctx->ssp_cmd.nb_bits_per_slots = config->bits_per_slot;
+ ctx->ssp_cmd.nb_slots = config->slots;
+ ctx->ssp_cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+ ctx->ssp_cmd.duplex = config->duplex;
+ ctx->ssp_cmd.active_tx_slot_map = config->active_slot_map;
+ ctx->ssp_cmd.active_rx_slot_map = config->active_slot_map;
+ ctx->ssp_cmd.frame_sync_frequency = config->fs_frequency;
+ ctx->ssp_cmd.frame_sync_polarity = config->frame_sync_polarity;
+ ctx->ssp_cmd.data_polarity = config->data_polarity;
+ ctx->ssp_cmd.frame_sync_width = config->fs_width;
+ ctx->ssp_cmd.ssp_protocol = config->ssp_protocol;
+ ctx->ssp_cmd.start_delay = config->start_delay;
+ ctx->ssp_cmd.reserved1 = ctx->ssp_cmd.reserved2 = 0xFF;
+}
+
int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
{
- struct sst_cmd_sba_hw_set_ssp cmd;
struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
const struct sst_ssp_config *config;
dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
- SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
- cmd.header.command_id = SBA_HW_SET_SSP;
- cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+ SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);
+ drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;
+ drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
- sizeof(struct sst_dsp_header);
config = &sst_ssp_configs;
dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
if (enable)
- cmd.switch_state = SST_SWITCH_ON;
+ drv->ssp_cmd.switch_state = SST_SWITCH_ON;
else
- cmd.switch_state = SST_SWITCH_OFF;
-
- cmd.selection = config->ssp_id;
- cmd.nb_bits_per_slots = config->bits_per_slot;
- cmd.nb_slots = config->slots;
- cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
- cmd.duplex = config->duplex;
- cmd.active_tx_slot_map = config->active_slot_map;
- cmd.active_rx_slot_map = config->active_slot_map;
- cmd.frame_sync_frequency = config->fs_frequency;
- cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
- cmd.data_polarity = 1;
- cmd.frame_sync_width = config->fs_width;
- cmd.ssp_protocol = config->ssp_protocol;
- cmd.start_delay = config->start_delay;
- cmd.reserved1 = cmd.reserved2 = 0xFF;
+ drv->ssp_cmd.switch_state = SST_SWITCH_OFF;
return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
- SST_TASK_SBA, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
+ SST_TASK_SBA, 0, &drv->ssp_cmd,
+ sizeof(drv->ssp_cmd.header) + drv->ssp_cmd.header.length);
}
static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
@@ -1280,36 +1401,32 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
down_read(&card->controls_rwsem);
list_for_each_entry(kctl, &card->controls, list) {
- idx = strstr(kctl->id.name, " ");
+ idx = strchr(kctl->id.name, ' ');
if (idx == NULL)
continue;
- index = strlen(kctl->id.name) - strlen(idx);
+ index = idx - (char*)kctl->id.name;
+ if (strncmp(kctl->id.name, w->name, index))
+ continue;
- if (strstr(kctl->id.name, "Volume") &&
- !strncmp(kctl->id.name, w->name, index))
+ if (strstr(kctl->id.name, "Volume"))
ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
- else if (strstr(kctl->id.name, "params") &&
- !strncmp(kctl->id.name, w->name, index))
+ else if (strstr(kctl->id.name, "params"))
ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
else if (strstr(kctl->id.name, "Switch") &&
- !strncmp(kctl->id.name, w->name, index) &&
strstr(kctl->id.name, "Gain")) {
struct sst_gain_mixer_control *mc =
(void *)kctl->private_value;
mc->w = w;
- } else if (strstr(kctl->id.name, "interleaver") &&
- !strncmp(kctl->id.name, w->name, index)) {
+ } else if (strstr(kctl->id.name, "interleaver")) {
struct sst_enum *e = (void *)kctl->private_value;
e->w = w;
- } else if (strstr(kctl->id.name, "deinterleaver") &&
- !strncmp(kctl->id.name, w->name, index)) {
-
+ } else if (strstr(kctl->id.name, "deinterleaver")) {
struct sst_enum *e = (void *)kctl->private_value;
e->w = w;
diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h
index daecc58f28af..93de8045d4e1 100644
--- a/sound/soc/intel/atom/sst-atom-controls.h
+++ b/sound/soc/intel/atom/sst-atom-controls.h
@@ -562,6 +562,8 @@ struct sst_ssp_config {
u8 active_slot_map;
u8 start_delay;
u16 fs_width;
+ u8 frame_sync_polarity;
+ u8 data_polarity;
};
struct sst_ssp_cfg {
@@ -695,7 +697,7 @@ struct sst_gain_mixer_control {
u16 module_id;
u16 pipe_id;
u16 task_id;
- char pname[44];
+ char pname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
struct snd_soc_dapm_widget *w;
};
@@ -867,4 +869,9 @@ struct sst_enum {
SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width);
+int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt);
+void sst_fill_ssp_defaults(struct snd_soc_dai *dai);
+
#endif
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 2fbaf2c75d17..641ebe61dc08 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -434,13 +434,51 @@ static int sst_enable_ssp(struct snd_pcm_substream *substream,
if (!dai->active) {
ret = sst_handle_vb_timer(dai, true);
- if (ret)
- return ret;
- ret = send_ssp_cmd(dai, dai->name, 1);
+ sst_fill_ssp_defaults(dai);
}
return ret;
}
+static int sst_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ if (dai->active == 1)
+ ret = send_ssp_cmd(dai, dai->name, 1);
+ return ret;
+}
+
+static int sst_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int ret = 0;
+
+ if (!dai->active)
+ return 0;
+
+ ret = sst_fill_ssp_config(dai, fmt);
+ if (ret < 0)
+ dev_err(dai->dev, "sst_set_format failed..\n");
+
+ return ret;
+}
+
+static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width) {
+ int ret = 0;
+
+ if (!dai->active)
+ return ret;
+
+ ret = sst_fill_ssp_slot(dai, tx_mask, rx_mask, slots, slot_width);
+ if (ret < 0)
+ dev_err(dai->dev, "sst_fill_ssp_slot failed..%d\n", ret);
+
+ return ret;
+}
+
static void sst_disable_ssp(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -465,6 +503,9 @@ static struct snd_soc_dai_ops sst_compr_dai_ops = {
static struct snd_soc_dai_ops sst_be_dai_ops = {
.startup = sst_enable_ssp,
+ .hw_params = sst_be_hw_params,
+ .set_fmt = sst_set_format,
+ .set_tdm_slot = sst_platform_set_ssp_slot,
.shutdown = sst_disable_ssp,
};
diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h
index 9094314be2b0..2409b23eeacf 100644
--- a/sound/soc/intel/atom/sst-mfld-platform.h
+++ b/sound/soc/intel/atom/sst-mfld-platform.h
@@ -22,6 +22,7 @@
#define __SST_PLATFORMDRV_H__
#include "sst-mfld-dsp.h"
+#include "sst-atom-controls.h"
extern struct sst_device *sst;
@@ -175,6 +176,7 @@ struct sst_data {
struct snd_sst_bytes_v2 *byte_stream;
struct mutex lock;
struct snd_soc_card *soc_card;
+ struct sst_cmd_sba_hw_set_ssp ssp_cmd;
};
int sst_register_dsp(struct sst_device *sst);
int sst_unregister_dsp(struct sst_device *sst);
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index 96c2e420cce6..a4b458e77089 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -368,8 +368,8 @@ static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
* initialize by FW or driver when firmware is loaded
*/
spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
- sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
- sst_shim_write64(shim, SST_CSR, shim_regs->csr),
+ sst_shim_write64(shim, SST_IMRX, shim_regs->imrx);
+ sst_shim_write64(shim, SST_CSR, shim_regs->csr);
spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
}
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 05f693083911..bb19b5801466 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -354,6 +354,10 @@ static struct sst_machines sst_acpi_chv[] = {
&chv_platform_data },
{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
&chv_platform_data },
+ {"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
+ &chv_platform_data },
+ {"193C9890", "cht-bsw", "cht-bsw-max98090", NULL,
+ "intel/fw_sst_22a8.bin", &chv_platform_data },
{},
};
diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c
index 7b50a9d17ec1..620da1d1b9e3 100644
--- a/sound/soc/intel/atom/sst/sst_drv_interface.c
+++ b/sound/soc/intel/atom/sst/sst_drv_interface.c
@@ -533,7 +533,7 @@ static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
info->buffer_ptr = pointer_samples / substream->runtime->channels;
- info->pcm_delay = delay_frames / substream->runtime->channels;
+ info->pcm_delay = delay_frames;
dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
info->buffer_ptr, info->pcm_delay);
return 0;
diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
index a839dbfa5218..4c01bb43928d 100644
--- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c
+++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
@@ -679,6 +679,14 @@ static u64 byt_reply_msg_match(u64 header, u64 *mask)
return header;
}
+static bool byt_is_dsp_busy(struct sst_dsp *dsp)
+{
+ u64 ipcx;
+
+ ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
+ return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
+}
+
int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt;
@@ -699,6 +707,9 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
ipc->ops.shim_dbg = byt_shim_dbg;
ipc->ops.tx_data_copy = byt_tx_data_copy;
ipc->ops.reply_msg_match = byt_reply_msg_match;
+ ipc->ops.is_dsp_busy = byt_is_dsp_busy;
+ ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
+ ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
err = sst_ipc_init(ipc);
if (err != 0)
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index f8237f0044eb..cb94895c9edb 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -5,6 +5,7 @@ snd-soc-sst-broadwell-objs := broadwell.o
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
+snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
@@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
new file mode 100644
index 000000000000..d604ee80eda4
--- /dev/null
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -0,0 +1,348 @@
+/*
+ * cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based
+ * platforms Cherrytrail and Braswell, with max98090 & TI codec.
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Fang, Yang A <yang.a.fang@intel.com>
+ * This file is modified from cht_bsw_rt5645.c
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/max98090.h"
+#include "../atom/sst-atom-controls.h"
+#include "../../codecs/ts3a227e.h"
+
+#define CHT_PLAT_CLK_3_HZ 19200000
+#define CHT_CODEC_DAI "HiFi"
+
+struct cht_mc_private {
+ struct snd_soc_jack jack;
+ bool ts3a227e_present;
+};
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = card->rtd + i;
+ if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+ strlen(CHT_CODEC_DAI)))
+ return rtd->codec_dai;
+ }
+ return NULL;
+}
+
+static const struct snd_soc_dapm_widget cht_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("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+ {"IN34", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "MICBIAS"},
+ {"DMICL", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPL"},
+ {"Headphone", NULL, "HPR"},
+ {"Ext Spk", NULL, "SPKL"},
+ {"Ext Spk", NULL, "SPKR"},
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx" },
+ {"codec_in1", NULL, "ssp2 Rx" },
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK,
+ CHT_PLAT_CLK_3_HZ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+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;
+
+ 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);
+ } else {
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin(&codec->dapm, "SHDN");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return 0;
+}
+
+static struct notifier_block cht_jack_nb = {
+ .notifier_call = cht_ti_jack_event,
+};
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ int jack_type;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_jack *jack = &ctx->jack;
+
+ /**
+ * TI supports 4 butons headset detection
+ * KEY_MEDIA
+ * KEY_VOICECOMMAND
+ * KEY_VOLUMEUP
+ * KEY_VOLUMEDOWN
+ */
+ if (ctx->ts3a227e_present)
+ jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ else
+ jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
+
+ ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
+ jack_type, jack, NULL, 0);
+
+ if (ret) {
+ dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ if (ctx->ts3a227e_present)
+ snd_soc_jack_notifier_register(jack, &cht_jack_nb);
+
+ return ret;
+}
+
+static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret = 0;
+ unsigned int fmt = 0;
+
+ ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret);
+ return ret;
+ }
+
+ fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS;
+
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret);
+ return ret;
+ }
+
+ /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static int cht_max98090_headset_init(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = component->card;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ return ts3a227e_enable_jack_detect(component, &ctx->jack);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+ .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+ .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_aux_dev cht_max98090_headset_dev = {
+ .name = "Headset Chip",
+ .init = cht_max98090_headset_init,
+ .codec_name = "i2c-104C227E:00",
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .nonatomic = true,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Compressed Port",
+ .stream_name = "Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .be_id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "HiFi",
+ .codec_name = "i2c-193C9890:00",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .init = cht_codec_init,
+ .be_hw_params_fixup = cht_codec_fixup,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_be_ssp2_ops,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+ .name = "chtmax98090",
+ .dai_link = cht_dailink,
+ .num_links = ARRAY_SIZE(cht_dailink),
+ .aux_dev = &cht_max98090_headset_dev,
+ .num_aux_devs = 1,
+ .dapm_widgets = cht_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+ .dapm_routes = cht_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+ .controls = cht_mc_controls,
+ .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ *(bool *)context = true;
+ return AE_OK;
+}
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+ bool found = false;
+ struct cht_mc_private *drv;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+ if (!drv)
+ return -ENOMEM;
+
+ if (ACPI_SUCCESS(acpi_get_devices(
+ "104C227E",
+ snd_acpi_codec_match,
+ &found, NULL)) && found) {
+ drv->ts3a227e_present = true;
+ } else {
+ /* no need probe TI jack detection chip */
+ snd_soc_card_cht.aux_dev = NULL;
+ snd_soc_card_cht.num_aux_devs = 0;
+ drv->ts3a227e_present = false;
+ }
+
+ /* register the soc card */
+ snd_soc_card_cht.dev = &pdev->dev;
+ snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_cht);
+ return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+ .driver = {
+ .name = "cht-bsw-max98090",
+ },
+ .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
+MODULE_AUTHOR("Fang, Yang A <yang.a.fang@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-max98090");
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 20a28b22e30f..bdcaf467842a 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -21,6 +21,7 @@
*/
#include <linux/module.h>
+#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/pcm.h>
@@ -33,9 +34,15 @@
#define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5645-aif1"
+struct cht_acpi_card {
+ char *codec_id;
+ int codec_type;
+ struct snd_soc_card *soc_card;
+};
+
struct cht_mc_private {
- struct snd_soc_jack hp_jack;
- struct snd_soc_jack mic_jack;
+ struct snd_soc_jack jack;
+ struct cht_acpi_card *acpi_card;
};
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
@@ -94,7 +101,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
platform_clock_control, SND_SOC_DAPM_POST_PMD),
};
-static const struct snd_soc_dapm_route cht_audio_map[] = {
+static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
{"IN1P", NULL, "Headset Mic"},
{"IN1N", NULL, "Headset Mic"},
{"DMIC L1", NULL, "Int Mic"},
@@ -115,6 +122,27 @@ static const struct snd_soc_dapm_route cht_audio_map[] = {
{"Ext Spk", NULL, "Platform Clock"},
};
+static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+ {"DMIC L2", NULL, "Int Mic"},
+ {"DMIC R2", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Ext Spk", NULL, "SPOL"},
+ {"Ext Spk", NULL, "SPOR"},
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx" },
+ {"codec_in1", NULL, "ssp2 Rx" },
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
+};
+
static const struct snd_kcontrol_new cht_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -150,6 +178,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
+ int jack_type;
struct snd_soc_codec *codec = runtime->codec;
struct snd_soc_dai *codec_dai = runtime->codec_dai;
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
@@ -169,23 +198,22 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- ret = snd_soc_card_jack_new(runtime->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &ctx->hp_jack,
- NULL, 0);
- if (ret) {
- dev_err(runtime->dev, "HP jack creation failed %d\n", ret);
- return ret;
- }
+ if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)
+ jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ else
+ jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
- ret = snd_soc_card_jack_new(runtime->card, "Mic Jack",
- SND_JACK_MICROPHONE, &ctx->mic_jack,
+ ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
+ jack_type, &ctx->jack,
NULL, 0);
if (ret) {
- dev_err(runtime->dev, "Mic jack creation failed %d\n", ret);
+ dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
return ret;
}
- rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack);
+ rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
return ret;
}
@@ -239,7 +267,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
+ .nonatomic = true,
.dynamic = 1,
.dpcm_playback = 1,
.dpcm_capture = 1,
@@ -267,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
| SND_SOC_DAIFMT_CBS_CFS,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
- .ignore_suspend = 1,
+ .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.ops = &cht_be_ssp2_ops,
@@ -275,43 +303,85 @@ static struct snd_soc_dai_link cht_dailink[] = {
};
/* SoC card */
-static struct snd_soc_card snd_soc_card_cht = {
+static struct snd_soc_card snd_soc_card_chtrt5645 = {
.name = "chtrt5645",
.dai_link = cht_dailink,
.num_links = ARRAY_SIZE(cht_dailink),
.dapm_widgets = cht_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
- .dapm_routes = cht_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+ .dapm_routes = cht_rt5645_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_rt5645_audio_map),
.controls = cht_mc_controls,
.num_controls = ARRAY_SIZE(cht_mc_controls),
};
+static struct snd_soc_card snd_soc_card_chtrt5650 = {
+ .name = "chtrt5650",
+ .dai_link = cht_dailink,
+ .num_links = ARRAY_SIZE(cht_dailink),
+ .dapm_widgets = cht_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+ .dapm_routes = cht_rt5650_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_rt5650_audio_map),
+ .controls = cht_mc_controls,
+ .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static struct cht_acpi_card snd_soc_cards[] = {
+ {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
+};
+
+static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ *(bool *)context = true;
+ return AE_OK;
+}
+
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
+ int i;
struct cht_mc_private *drv;
+ struct snd_soc_card *card = snd_soc_cards[0].soc_card;
+ bool found = false;
+ char codec_name[16];
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
return -ENOMEM;
- snd_soc_card_cht.dev = &pdev->dev;
- snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
+ if (ACPI_SUCCESS(acpi_get_devices(
+ snd_soc_cards[i].codec_id,
+ snd_acpi_codec_match,
+ &found, NULL)) && found) {
+ dev_dbg(&pdev->dev,
+ "found codec %s\n", snd_soc_cards[i].codec_id);
+ card = snd_soc_cards[i].soc_card;
+ drv->acpi_card = &snd_soc_cards[i];
+ break;
+ }
+ }
+ card->dev = &pdev->dev;
+ sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+ /* set correct codec name */
+ strcpy((char *)card->dai_link[2].codec_name, codec_name);
+ snd_soc_card_set_drvdata(card, drv);
+ ret_val = devm_snd_soc_register_card(&pdev->dev, card);
if (ret_val) {
dev_err(&pdev->dev,
"snd_soc_register_card failed %d\n", ret_val);
return ret_val;
}
- platform_set_drvdata(pdev, &snd_soc_card_cht);
+ platform_set_drvdata(pdev, card);
return ret_val;
}
static struct platform_driver snd_cht_mc_driver = {
.driver = {
.name = "cht-bsw-rt5645",
- .pm = &snd_soc_pm_ops,
},
.probe = snd_cht_mc_probe,
};
diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c
index 42f293f9c6e2..67b6d3d52f57 100644
--- a/sound/soc/intel/common/sst-acpi.c
+++ b/sound/soc/intel/common/sst-acpi.c
@@ -263,7 +263,7 @@ static struct sst_acpi_desc sst_acpi_baytrail_desc = {
.resindex_dma_base = -1,
};
-static struct acpi_device_id sst_acpi_match[] = {
+static const struct acpi_device_id sst_acpi_match[] = {
{ "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
{ "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
{ "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c
index 4b62a553823c..a12c7bb08d3b 100644
--- a/sound/soc/intel/common/sst-ipc.c
+++ b/sound/soc/intel/common/sst-ipc.c
@@ -129,11 +129,31 @@ static int msg_empty_list_init(struct sst_generic_ipc *ipc)
return -ENOMEM;
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+ ipc->msg[i].tx_data = kzalloc(ipc->tx_data_max_size, GFP_KERNEL);
+ if (ipc->msg[i].tx_data == NULL)
+ goto free_mem;
+
+ ipc->msg[i].rx_data = kzalloc(ipc->rx_data_max_size, GFP_KERNEL);
+ if (ipc->msg[i].rx_data == NULL) {
+ kfree(ipc->msg[i].tx_data);
+ goto free_mem;
+ }
+
init_waitqueue_head(&ipc->msg[i].waitq);
list_add(&ipc->msg[i].list, &ipc->empty_list);
}
return 0;
+
+free_mem:
+ while (i > 0) {
+ kfree(ipc->msg[i-1].tx_data);
+ kfree(ipc->msg[i-1].rx_data);
+ --i;
+ }
+ kfree(ipc->msg);
+
+ return -ENOMEM;
}
static void ipc_tx_msgs(struct kthread_work *work)
@@ -142,7 +162,6 @@ static void ipc_tx_msgs(struct kthread_work *work)
container_of(work, struct sst_generic_ipc, kwork);
struct ipc_message *msg;
unsigned long flags;
- u64 ipcx;
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
@@ -153,8 +172,8 @@ static void ipc_tx_msgs(struct kthread_work *work)
/* if the DSP is busy, we will TX messages after IRQ.
* also postpone if we are in the middle of procesing completion irq*/
- ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX);
- if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
+ if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
+ dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return;
}
@@ -280,11 +299,18 @@ EXPORT_SYMBOL_GPL(sst_ipc_init);
void sst_ipc_fini(struct sst_generic_ipc *ipc)
{
+ int i;
+
if (ipc->tx_thread)
kthread_stop(ipc->tx_thread);
- if (ipc->msg)
+ if (ipc->msg) {
+ for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+ kfree(ipc->msg[i].tx_data);
+ kfree(ipc->msg[i].rx_data);
+ }
kfree(ipc->msg);
+ }
}
EXPORT_SYMBOL_GPL(sst_ipc_fini);
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h
index 125ea451a373..ceb7e468a3fa 100644
--- a/sound/soc/intel/common/sst-ipc.h
+++ b/sound/soc/intel/common/sst-ipc.h
@@ -32,9 +32,9 @@ struct ipc_message {
u64 header;
/* direction wrt host CPU */
- char tx_data[IPC_MAX_MAILBOX_BYTES];
+ char *tx_data;
size_t tx_size;
- char rx_data[IPC_MAX_MAILBOX_BYTES];
+ char *rx_data;
size_t rx_size;
wait_queue_head_t waitq;
@@ -51,6 +51,7 @@ struct sst_plat_ipc_ops {
void (*shim_dbg)(struct sst_generic_ipc *, const char *);
void (*tx_data_copy)(struct ipc_message *, char *, size_t);
u64 (*reply_msg_match)(u64 header, u64 *mask);
+ bool (*is_dsp_busy)(struct sst_dsp *dsp);
};
/* SST generic IPC data */
@@ -68,6 +69,8 @@ struct sst_generic_ipc {
struct kthread_work kwork;
bool pending;
struct ipc_message *msg;
+ int tx_data_max_size;
+ int rx_data_max_size;
struct sst_plat_ipc_ops ops;
};
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
index 324eceb07b25..f95f271aab0c 100644
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -2098,6 +2098,14 @@ static u64 hsw_reply_msg_match(u64 header, u64 *mask)
return header;
}
+static bool hsw_is_dsp_busy(struct sst_dsp *dsp)
+{
+ u64 ipcx;
+
+ ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
+ return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
+}
+
int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
{
struct sst_hsw_ipc_fw_version version;
@@ -2117,6 +2125,10 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
ipc->ops.shim_dbg = hsw_shim_dbg;
ipc->ops.tx_data_copy = hsw_tx_data_copy;
ipc->ops.reply_msg_match = hsw_reply_msg_match;
+ ipc->ops.is_dsp_busy = hsw_is_dsp_busy;
+
+ ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
+ ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
ret = sst_ipc_init(ipc);
if (ret != 0)
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
index 23ae0400d6db..1aa819c7e09b 100644
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/sound/soc/intel/haswell/sst-haswell-pcm.c
@@ -928,10 +928,15 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
- sst_hsw_runtime_module_free(pcm_data->runtime);
+ if (pcm_data->runtime){
+ sst_hsw_runtime_module_free(pcm_data->runtime);
+ pcm_data->runtime = NULL;
+ }
}
- if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+ if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES) &&
+ pdata->runtime_waves) {
sst_hsw_runtime_module_free(pdata->runtime_waves);
+ pdata->runtime_waves = NULL;
}
}
@@ -1204,6 +1209,20 @@ static int hsw_pcm_runtime_idle(struct device *dev)
return 0;
}
+static int hsw_pcm_suspend(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+
+ /* enter D3 state and stall */
+ sst_hsw_dsp_runtime_suspend(hsw);
+ /* free all runtime modules */
+ hsw_pcm_free_modules(pdata);
+ /* put the DSP to sleep, fw unloaded after runtime modules freed */
+ sst_hsw_dsp_runtime_sleep(hsw);
+ return 0;
+}
+
static int hsw_pcm_runtime_suspend(struct device *dev)
{
struct hsw_priv_data *pdata = dev_get_drvdata(dev);
@@ -1220,8 +1239,7 @@ static int hsw_pcm_runtime_suspend(struct device *dev)
return ret;
sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
}
- sst_hsw_dsp_runtime_suspend(hsw);
- sst_hsw_dsp_runtime_sleep(hsw);
+ hsw_pcm_suspend(dev);
pdata->pm_state = HSW_PM_STATE_RTD3;
return 0;
@@ -1328,7 +1346,6 @@ static void hsw_pcm_complete(struct device *dev)
static int hsw_pcm_prepare(struct device *dev)
{
struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
struct hsw_pcm_data *pcm_data;
int i, err;
@@ -1361,10 +1378,7 @@ static int hsw_pcm_prepare(struct device *dev)
if (err < 0)
dev_err(dev, "failed to save context for PCM %d\n", i);
}
- /* enter D3 state and stall */
- sst_hsw_dsp_runtime_suspend(hsw);
- /* put the DSP to sleep */
- sst_hsw_dsp_runtime_sleep(hsw);
+ hsw_pcm_suspend(dev);
}
snd_soc_suspend(pdata->soc_card->dev);
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
new file mode 100644
index 000000000000..15c04e2eae34
--- /dev/null
+++ b/sound/soc/mediatek/Kconfig
@@ -0,0 +1,30 @@
+config SND_SOC_MEDIATEK
+ tristate "ASoC support for Mediatek chip"
+ depends on ARCH_MEDIATEK
+ help
+ This adds ASoC platform driver support for Mediatek chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ Ex: MT8173
+
+config SND_SOC_MT8173_MAX98090
+ tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
+ depends on SND_SOC_MEDIATEK
+ select SND_SOC_MAX98090
+ help
+ This adds ASoC driver for Mediatek MT8173 boards
+ with the MAX98090 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8173_RT5650_RT5676
+ tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
+ depends on SND_SOC_MEDIATEK
+ select SND_SOC_RT5645
+ select SND_SOC_RT5677
+ help
+ This adds ASoC driver for Mediatek MT8173 boards
+ with the RT5650 and RT5676 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
new file mode 100644
index 000000000000..75effbec438d
--- /dev/null
+++ b/sound/soc/mediatek/Makefile
@@ -0,0 +1,5 @@
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c
new file mode 100644
index 000000000000..4d44b5803e55
--- /dev/null
+++ b/sound/soc/mediatek/mt8173-max98090.c
@@ -0,0 +1,213 @@
+/*
+ * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.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 and
+ * only 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 <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include "../codecs/max98090.h"
+
+static struct snd_soc_jack mt8173_max98090_jack;
+
+static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_max98090_routes[] = {
+ {"Speaker", NULL, "SPKL"},
+ {"Speaker", NULL, "SPKR"},
+ {"DMICL", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPL"},
+ {"Headphone", NULL, "HPR"},
+ {"Headset Mic", NULL, "MICBIAS"},
+ {"IN34", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_max98090_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256,
+ SND_SOC_CLOCK_IN);
+}
+
+static struct snd_soc_ops mt8173_max98090_ops = {
+ .hw_params = mt8173_max98090_hw_params,
+};
+
+static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_codec *codec = runtime->codec;
+
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE,
+ &mt8173_max98090_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_jack_add_pins(&mt8173_max98090_jack,
+ ARRAY_SIZE(mt8173_max98090_jack_pins),
+ mt8173_max98090_jack_pins);
+ if (ret) {
+ dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret);
+ return ret;
+ }
+
+ return max98090_mic_detect(codec, &mt8173_max98090_jack);
+}
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_max98090_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "MAX98090 Playback",
+ .stream_name = "MAX98090 Playback",
+ .cpu_dai_name = "DL1",
+ .platform_name = "11220000.mt8173-afe-pcm",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "MAX98090 Capture",
+ .stream_name = "MAX98090 Capture",
+ .cpu_dai_name = "VUL",
+ .platform_name = "11220000.mt8173-afe-pcm",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ /* Back End DAI links */
+ {
+ .name = "Codec",
+ .cpu_dai_name = "I2S",
+ .platform_name = "11220000.mt8173-afe-pcm",
+ .no_pcm = 1,
+ .codec_dai_name = "HiFi",
+ .init = mt8173_max98090_init,
+ .ops = &mt8173_max98090_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+static struct snd_soc_card mt8173_max98090_card = {
+ .name = "mt8173-max98090",
+ .dai_link = mt8173_max98090_dais,
+ .num_links = ARRAY_SIZE(mt8173_max98090_dais),
+ .controls = mt8173_max98090_controls,
+ .num_controls = ARRAY_SIZE(mt8173_max98090_controls),
+ .dapm_widgets = mt8173_max98090_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets),
+ .dapm_routes = mt8173_max98090_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes),
+};
+
+static int mt8173_max98090_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8173_max98090_card;
+ struct device_node *codec_node;
+ int ret, i;
+
+ codec_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,audio-codec", 0);
+ if (!codec_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < card->num_links; i++) {
+ if (mt8173_max98090_dais[i].codec_name)
+ continue;
+ mt8173_max98090_dais[i].codec_of_node = codec_node;
+ }
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int mt8173_max98090_dev_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 mt8173_max98090_dt_match[] = {
+ { .compatible = "mediatek,mt8173-max98090", },
+ { }
+};
+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,
+#endif
+ },
+ .probe = mt8173_max98090_dev_probe,
+ .remove = mt8173_max98090_dev_remove,
+};
+
+module_platform_driver(mt8173_max98090_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mt8173-max98090");
+
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
new file mode 100644
index 000000000000..094055323059
--- /dev/null
+++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
@@ -0,0 +1,278 @@
+/*
+ * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.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 and
+ * only 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/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/rt5645.h"
+#include "../codecs/rt5677.h"
+
+#define MCLK_FOR_CODECS 12288000
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = {
+ {"Speaker", NULL, "SPOL"},
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */
+ {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */
+ {"Sub DMIC R1", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */
+ {"Headset Mic", NULL, "micbias1"},
+ {"Headset Mic", NULL, "micbias2"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+ {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int i, ret;
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+
+ /* pll from mclk 12.288M */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+ params_rate(params) * 512);
+ if (ret)
+ return ret;
+
+ /* sysclk from pll */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
+ .hw_params = mt8173_rt5650_rt5676_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_rt5676_jack;
+
+static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
+ struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec;
+ int ret;
+
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ rt5677_sel_asrc_clk_src(codec_sub,
+ RT5677_DA_STEREO_FILTER |
+ RT5677_AD_STEREO1_FILTER,
+ RT5677_CLK_SEL_I2S1_ASRC);
+ rt5677_sel_asrc_clk_src(codec_sub,
+ RT5677_AD_STEREO2_FILTER |
+ RT5677_I2S2_SOURCE,
+ RT5677_CLK_SEL_I2S2_ASRC);
+
+ /* enable 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,
+ &mt8173_rt5650_rt5676_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+ return ret;
+ }
+
+ return rt5645_set_jack_detect(codec,
+ &mt8173_rt5650_rt5676_jack,
+ &mt8173_rt5650_rt5676_jack,
+ &mt8173_rt5650_rt5676_jack);
+}
+
+static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
+ {
+ .dai_name = "rt5645-aif1",
+ },
+ {
+ .dai_name = "rt5677-aif1",
+ },
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "rt5650_rt5676 Playback",
+ .stream_name = "rt5650_rt5676 Playback",
+ .cpu_dai_name = "DL1",
+ .platform_name = "11220000.mt8173-afe-pcm",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "rt5650_rt5676 Capture",
+ .stream_name = "rt5650_rt5676 Capture",
+ .cpu_dai_name = "VUL",
+ .platform_name = "11220000.mt8173-afe-pcm",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ .name = "Codec",
+ .cpu_dai_name = "I2S",
+ .platform_name = "11220000.mt8173-afe-pcm",
+ .no_pcm = 1,
+ .codecs = mt8173_rt5650_rt5676_codecs,
+ .num_codecs = 2,
+ .init = mt8173_rt5650_rt5676_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &mt8173_rt5650_rt5676_ops,
+ .ignore_pmdown_time = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+ .name = "rt5650_rt5676 intercodec",
+ .stream_name = "rt5650_rt5676 intercodec",
+ .cpu_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .codec_dai_name = "rt5677-aif2",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ },
+
+};
+
+static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = {
+ {
+ .name_prefix = "Sub",
+ },
+};
+
+static struct snd_soc_card mt8173_rt5650_rt5676_card = {
+ .name = "mtk-rt5650-rt5676",
+ .dai_link = mt8173_rt5650_rt5676_dais,
+ .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais),
+ .codec_conf = mt8173_rt5650_rt5676_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf),
+ .controls = mt8173_rt5650_rt5676_controls,
+ .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls),
+ .dapm_widgets = mt8173_rt5650_rt5676_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets),
+ .dapm_routes = mt8173_rt5650_rt5676_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes),
+};
+
+static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8173_rt5650_rt5676_card;
+ int ret;
+
+ mt8173_rt5650_rt5676_codecs[0].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+ if (!mt8173_rt5650_rt5676_codecs[0].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ mt8173_rt5650_rt5676_codecs[1].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+ if (!mt8173_rt5650_rt5676_codecs[1].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ mt8173_rt5650_rt5676_codec_conf[0].of_node =
+ mt8173_rt5650_rt5676_codecs[1].of_node;
+
+ mt8173_rt5650_rt5676_dais[3].codec_of_node =
+ mt8173_rt5650_rt5676_codecs[1].of_node;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+
+ ret = snd_soc_register_card(card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int mt8173_rt5650_rt5676_dev_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 mt8173_rt5650_rt5676_dt_match[] = {
+ { .compatible = "mediatek,mt8173-rt5650-rt5676", },
+ { }
+};
+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,
+#endif
+ },
+ .probe = mt8173_rt5650_rt5676_dev_probe,
+ .remove = mt8173_rt5650_rt5676_dev_remove,
+};
+
+module_platform_driver(mt8173_rt5650_rt5676_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650-rt5676");
+
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
new file mode 100644
index 000000000000..a88b17511fdf
--- /dev/null
+++ b/sound/soc/mediatek/mtk-afe-common.h
@@ -0,0 +1,109 @@
+/*
+ * mtk_afe_common.h -- Mediatek audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Hidalgo Huang <hidalgo.huang@mediatek.com>
+ * Ir Lian <ir.lian@mediatek.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 and
+ * only 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 _MTK_AFE_COMMON_H_
+#define _MTK_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+ MTK_AFE_MEMIF_DL1,
+ MTK_AFE_MEMIF_DL2,
+ MTK_AFE_MEMIF_VUL,
+ MTK_AFE_MEMIF_DAI,
+ MTK_AFE_MEMIF_AWB,
+ MTK_AFE_MEMIF_MOD_DAI,
+ MTK_AFE_MEMIF_HDMI,
+ MTK_AFE_MEMIF_NUM,
+ MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
+ MTK_AFE_IO_MOD_PCM2,
+ MTK_AFE_IO_PMIC,
+ MTK_AFE_IO_I2S,
+ MTK_AFE_IO_2ND_I2S,
+ MTK_AFE_IO_HW_GAIN1,
+ MTK_AFE_IO_HW_GAIN2,
+ MTK_AFE_IO_MRG_O,
+ MTK_AFE_IO_MRG_I,
+ MTK_AFE_IO_DAIBT,
+ MTK_AFE_IO_HDMI,
+};
+
+enum {
+ MTK_AFE_IRQ_1,
+ MTK_AFE_IRQ_2,
+ MTK_AFE_IRQ_3,
+ MTK_AFE_IRQ_4,
+ MTK_AFE_IRQ_5,
+ MTK_AFE_IRQ_6,
+ MTK_AFE_IRQ_7,
+ MTK_AFE_IRQ_8,
+ MTK_AFE_IRQ_NUM,
+};
+
+enum {
+ MTK_CLK_INFRASYS_AUD,
+ MTK_CLK_TOP_PDN_AUD,
+ MTK_CLK_TOP_PDN_AUD_BUS,
+ MTK_CLK_I2S0_M,
+ MTK_CLK_I2S1_M,
+ MTK_CLK_I2S2_M,
+ MTK_CLK_I2S3_M,
+ MTK_CLK_I2S3_B,
+ MTK_CLK_BCK0,
+ MTK_CLK_BCK1,
+ MTK_CLK_NUM
+};
+
+struct mtk_afe;
+struct snd_pcm_substream;
+
+struct mtk_afe_memif_data {
+ int id;
+ const char *name;
+ int reg_ofs_base;
+ int reg_ofs_cur;
+ int fs_shift;
+ int mono_shift;
+ int enable_shift;
+ int irq_reg_cnt;
+ int irq_cnt_shift;
+ int irq_en_shift;
+ int irq_fs_shift;
+ int irq_clr_shift;
+};
+
+struct mtk_afe_memif {
+ unsigned int phys_buf_addr;
+ int buffer_size;
+ unsigned int hw_ptr; /* Previous IRQ's HW ptr */
+ struct snd_pcm_substream *substream;
+ const struct mtk_afe_memif_data *data;
+ 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
new file mode 100644
index 000000000000..cc228db5fb76
--- /dev/null
+++ b/sound/soc/mediatek/mtk-afe-pcm.c
@@ -0,0 +1,1233 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Hidalgo Huang <hidalgo.huang@mediatek.com>
+ * Ir Lian <ir.lian@mediatek.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 and
+ * only 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/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mtk-afe-common.h"
+
+/*****************************************************************************
+ * R E G I S T E R D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON1 0x0004
+#define AFE_DAC_CON0 0x0010
+#define AFE_DAC_CON1 0x0014
+#define AFE_I2S_CON1 0x0034
+#define AFE_I2S_CON2 0x0038
+#define AFE_CONN_24BIT 0x006c
+
+#define AFE_CONN1 0x0024
+#define AFE_CONN2 0x0028
+#define AFE_CONN7 0x0460
+#define AFE_CONN8 0x0464
+#define AFE_HDMI_CONN0 0x0390
+
+/* Memory interface */
+#define AFE_DL1_BASE 0x0040
+#define AFE_DL1_CUR 0x0044
+#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_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_ADDA2_TOP_CON0 0x0600
+
+#define AFE_HDMI_OUT_CON0 0x0370
+
+#define AFE_IRQ_MCU_CON 0x03a0
+#define AFE_IRQ_STATUS 0x03a4
+#define AFE_IRQ_CLR 0x03a8
+#define AFE_IRQ_CNT1 0x03ac
+#define AFE_IRQ_CNT2 0x03b0
+#define AFE_IRQ_MCU_EN 0x03b4
+#define AFE_IRQ_CNT5 0x03bc
+#define AFE_IRQ_CNT7 0x03dc
+
+#define AFE_TDM_CON1 0x0548
+#define AFE_TDM_CON2 0x054c
+
+#define AFE_BASE_END_OFFSET 8
+#define AFE_IRQ_STATUS_BITS 0xff
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUD_TCON0_PDN_SPDF (0x1 << 21)
+#define AUD_TCON0_PDN_HDMI (0x1 << 20)
+#define AUD_TCON0_PDN_24M (0x1 << 9)
+#define AUD_TCON0_PDN_22M (0x1 << 8)
+#define AUD_TCON0_PDN_AFE (0x1 << 2)
+
+/* AFE_I2S_CON1 (0x0034) */
+#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12)
+#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8)
+#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3)
+#define AFE_I2S_CON1_EN (0x1 << 0)
+
+/* AFE_I2S_CON2 (0x0038) */
+#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12)
+#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8)
+#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3)
+#define AFE_I2S_CON2_EN (0x1 << 0)
+
+/* AFE_CONN_24BIT (0x006c) */
+#define AFE_CONN_24BIT_O04 (0x1 << 4)
+#define AFE_CONN_24BIT_O03 (0x1 << 3)
+
+/* AFE_HDMI_CONN0 (0x0390) */
+#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21)
+#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18)
+#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15)
+#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12)
+#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9)
+#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6)
+#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3)
+#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0)
+
+/* AFE_TDM_CON1 (0x0548) */
+#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24)
+#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12)
+#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8)
+#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4)
+#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3)
+#define AFE_TDM_CON1_BCK_INV (0x1 << 1)
+#define AFE_TDM_CON1_EN (0x1 << 0)
+
+enum afe_tdm_ch_start {
+ AFE_TDM_CH_START_O30_O31 = 0,
+ AFE_TDM_CH_START_O32_O33,
+ AFE_TDM_CH_START_O34_O35,
+ AFE_TDM_CH_START_O36_O37,
+ AFE_TDM_CH_ZERO,
+};
+
+static const struct snd_pcm_hardware mtk_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 512,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .fifo_size = 0,
+};
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+
+ return bytes_to_frames(substream->runtime, memif->hw_ptr);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+ .ioctl = snd_pcm_lib_ioctl,
+ .pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ size = mtk_afe_hardware.buffer_bytes_max;
+
+ return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+ .ops = &mtk_afe_pcm_ops,
+ .pcm_new = mtk_afe_pcm_new,
+ .pcm_free = mtk_afe_pcm_free,
+};
+
+struct mtk_afe_rate {
+ unsigned int rate;
+ unsigned int regvalue;
+};
+
+static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+ { .rate = 8000, .regvalue = 0 },
+ { .rate = 11025, .regvalue = 1 },
+ { .rate = 12000, .regvalue = 2 },
+ { .rate = 16000, .regvalue = 4 },
+ { .rate = 22050, .regvalue = 5 },
+ { .rate = 24000, .regvalue = 6 },
+ { .rate = 32000, .regvalue = 8 },
+ { .rate = 44100, .regvalue = 9 },
+ { .rate = 48000, .regvalue = 10 },
+ { .rate = 88000, .regvalue = 11 },
+ { .rate = 96000, .regvalue = 12 },
+ { .rate = 174000, .regvalue = 13 },
+ { .rate = 192000, .regvalue = 14 },
+};
+
+static int mtk_afe_i2s_fs(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
+ if (mtk_afe_i2s_rates[i].rate == sample_rate)
+ return mtk_afe_i2s_rates[i].regvalue;
+
+ return -EINVAL;
+}
+
+static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+{
+ unsigned int val;
+ int fs = mtk_afe_i2s_fs(rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ /* from external ADC */
+ regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1);
+
+ /* set input */
+ val = AFE_I2S_CON2_LOW_JITTER_CLK |
+ AFE_I2S_CON2_RATE(fs) |
+ AFE_I2S_CON2_FORMAT_I2S;
+
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val);
+
+ /* set output */
+ val = AFE_I2S_CON1_LOW_JITTER_CLK |
+ AFE_I2S_CON1_RATE(fs) |
+ AFE_I2S_CON1_FORMAT_I2S;
+
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val);
+ return 0;
+}
+
+static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+{
+ unsigned int val;
+
+ regmap_read(afe->regmap, AFE_I2S_CON2, &val);
+ if (!!(val & AFE_I2S_CON2_EN) == enable)
+ return; /* must skip soft reset */
+
+ /* I2S soft reset begin */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4);
+
+ /* input */
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable);
+
+ /* output */
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
+
+ /* I2S soft reset end */
+ udelay(1);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0);
+}
+
+static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
+ struct clk *m_ck, struct clk *b_ck)
+{
+ int ret;
+
+ if (m_ck) {
+ ret = clk_prepare_enable(m_ck);
+ if (ret) {
+ dev_err(afe->dev, "Failed to enable m_ck\n");
+ return ret;
+ }
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
+ }
+
+ if (b_ck) {
+ ret = clk_prepare_enable(b_ck);
+ if (ret) {
+ dev_err(afe->dev, "Failed to enable b_ck\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
+ struct clk *m_ck, unsigned int mck_rate,
+ struct clk *b_ck, unsigned int bck_rate)
+{
+ int ret;
+
+ if (m_ck) {
+ ret = clk_set_rate(m_ck, mck_rate);
+ if (ret) {
+ dev_err(afe->dev, "Failed to set m_ck rate\n");
+ return ret;
+ }
+ }
+
+ if (b_ck) {
+ ret = clk_set_rate(b_ck, bck_rate);
+ if (ret) {
+ dev_err(afe->dev, "Failed to set b_ck rate\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
+ struct clk *m_ck, struct clk *b_ck)
+{
+ if (m_ck) {
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
+ AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
+ clk_disable_unprepare(m_ck);
+ }
+ if (b_ck)
+ clk_disable_unprepare(b_ck);
+}
+
+static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (dai->active)
+ return 0;
+
+ mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
+ return 0;
+}
+
+static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (dai->active)
+ return;
+
+ mtk_afe_set_i2s_enable(afe, false);
+ mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
+}
+
+static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int ret;
+
+ mtk_afe_dais_set_clks(afe,
+ afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
+ NULL, 0);
+ /* config I2S */
+ ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
+ if (ret)
+ return ret;
+
+ mtk_afe_set_i2s_enable(afe, true);
+
+ return 0;
+}
+
+static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (dai->active)
+ return 0;
+
+ mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
+ afe->clocks[MTK_CLK_I2S3_B]);
+ return 0;
+}
+
+static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (dai->active)
+ return;
+
+ mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
+ afe->clocks[MTK_CLK_I2S3_B]);
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
+}
+
+static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ unsigned int val;
+
+ mtk_afe_dais_set_clks(afe,
+ afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
+ afe->clocks[MTK_CLK_I2S3_B],
+ runtime->rate * runtime->channels * 32);
+
+ val = AFE_TDM_CON1_BCK_INV |
+ AFE_TDM_CON1_1_BCK_DELAY |
+ AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */
+ AFE_TDM_CON1_WLEN_32BIT |
+ AFE_TDM_CON1_32_BCK_CYCLES |
+ AFE_TDM_CON1_LRCK_WIDTH(32);
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val);
+
+ /* set tdm2 config */
+ switch (runtime->channels) {
+ case 1:
+ case 2:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_ZERO << 4);
+ val |= (AFE_TDM_CH_ZERO << 8);
+ val |= (AFE_TDM_CH_ZERO << 12);
+ break;
+ case 3:
+ case 4:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_START_O32_O33 << 4);
+ val |= (AFE_TDM_CH_ZERO << 8);
+ val |= (AFE_TDM_CH_ZERO << 12);
+ break;
+ case 5:
+ case 6:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_START_O32_O33 << 4);
+ val |= (AFE_TDM_CH_START_O34_O35 << 8);
+ val |= (AFE_TDM_CH_ZERO << 12);
+ break;
+ case 7:
+ case 8:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_START_O32_O33 << 4);
+ val |= (AFE_TDM_CH_START_O34_O35 << 8);
+ val |= (AFE_TDM_CH_START_O36_O37 << 12);
+ break;
+ default:
+ val = 0;
+ }
+ regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val);
+
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ 0x000000f0, runtime->channels << 4);
+ return 0;
+}
+
+static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0);
+
+ /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
+ regmap_write(afe->regmap, AFE_HDMI_CONN0,
+ AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
+ AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
+ AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
+ AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+
+ /* enable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
+
+ /* enable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1);
+
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0);
+
+ /* disable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0);
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
+ AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int ret;
+
+ memif->substream = substream;
+
+ snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+ return ret;
+}
+
+static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+
+ memif->substream = NULL;
+}
+
+static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int ret;
+
+ dev_dbg(afe->dev,
+ "%s period = %u, rate= %u, channels=%u\n",
+ __func__, params_period_size(params), params_rate(params),
+ params_channels(params));
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ memif->phys_buf_addr = substream->runtime->dma_addr;
+ memif->buffer_size = substream->runtime->dma_bytes;
+ memif->hw_ptr = 0;
+
+ /* start */
+ regmap_write(afe->regmap,
+ memif->data->reg_ofs_base, memif->phys_buf_addr);
+ /* end */
+ regmap_write(afe->regmap,
+ memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+ memif->phys_buf_addr + memif->buffer_size - 1);
+
+ /* set channel */
+ if (memif->data->mono_shift >= 0) {
+ unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+ regmap_update_bits(afe->regmap, AFE_DAC_CON1,
+ 1 << memif->data->mono_shift,
+ mono << memif->data->mono_shift);
+ }
+
+ /* set rate */
+ if (memif->data->fs_shift < 0)
+ return 0;
+ if (memif->data->id == MTK_AFE_MEMIF_DAI ||
+ memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
+ unsigned int val;
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0;
+ break;
+ case 16000:
+ val = 1;
+ break;
+ case 32000:
+ val = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (memif->data->id == MTK_AFE_MEMIF_DAI)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 0x3 << memif->data->fs_shift,
+ val << memif->data->fs_shift);
+ else
+ regmap_update_bits(afe->regmap, AFE_DAC_CON1,
+ 0x3 << memif->data->fs_shift,
+ val << memif->data->fs_shift);
+
+ } else {
+ int fs = mtk_afe_i2s_fs(params_rate(params));
+
+ if (fs < 0)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap, AFE_DAC_CON1,
+ 0xf << memif->data->fs_shift,
+ fs << memif->data->fs_shift);
+ }
+
+ return 0;
+}
+
+static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int mtk_afe_dais_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ /* enable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+ return 0;
+}
+
+static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ unsigned int counter = runtime->period_size;
+
+ dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (memif->data->enable_shift >= 0)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 1 << memif->data->enable_shift,
+ 1 << memif->data->enable_shift);
+
+ /* set irq counter */
+ regmap_update_bits(afe->regmap,
+ memif->data->irq_reg_cnt,
+ 0x3ffff << memif->data->irq_cnt_shift,
+ counter << memif->data->irq_cnt_shift);
+
+ /* set irq fs */
+ if (memif->data->irq_fs_shift >= 0) {
+ int fs = mtk_afe_i2s_fs(runtime->rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ AFE_IRQ_MCU_CON,
+ 0xf << memif->data->irq_fs_shift,
+ fs << memif->data->irq_fs_shift);
+ }
+ /* enable interrupt */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
+ 1 << memif->data->irq_en_shift,
+ 1 << memif->data->irq_en_shift);
+
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (memif->data->enable_shift >= 0)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 1 << memif->data->enable_shift, 0);
+ /* disable interrupt */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
+ 1 << memif->data->irq_en_shift,
+ 0 << memif->data->irq_en_shift);
+ /* and clear pending IRQ */
+ regmap_write(afe->regmap, AFE_IRQ_CLR,
+ 1 << memif->data->irq_clr_shift);
+ memif->hw_ptr = 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
+ .startup = mtk_afe_dais_startup,
+ .shutdown = mtk_afe_dais_shutdown,
+ .hw_params = mtk_afe_dais_hw_params,
+ .hw_free = mtk_afe_dais_hw_free,
+ .prepare = mtk_afe_dais_prepare,
+ .trigger = mtk_afe_dais_trigger,
+};
+
+/* BE DAIs */
+static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
+ .startup = mtk_afe_i2s_startup,
+ .shutdown = mtk_afe_i2s_shutdown,
+ .prepare = mtk_afe_i2s_prepare,
+};
+
+static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
+ .startup = mtk_afe_hdmi_startup,
+ .shutdown = mtk_afe_hdmi_shutdown,
+ .prepare = mtk_afe_hdmi_prepare,
+ .trigger = mtk_afe_hdmi_trigger,
+
+};
+
+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,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_dai_ops,
+ }, {
+ .name = "VUL", /* voice uplink */
+ .id = MTK_AFE_MEMIF_VUL,
+ .capture = {
+ .stream_name = "VUL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_dai_ops,
+ }, {
+ /* BE DAIs */
+ .name = "I2S",
+ .id = MTK_AFE_IO_I2S,
+ .playback = {
+ .stream_name = "I2S Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "I2S Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_i2s_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+ /* FE DAIs */
+ {
+ .name = "HDMI",
+ .id = MTK_AFE_MEMIF_HDMI,
+ .playback = {
+ .stream_name = "HDMI",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_dai_ops,
+ }, {
+ /* BE DAIs */
+ .name = "HDMIO",
+ .id = MTK_AFE_IO_HDMI,
+ .playback = {
+ .stream_name = "HDMIO Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_hdmi_ops,
+ },
+};
+
+static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
+};
+
+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),
+ SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
+ mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
+ SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
+ mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+ SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
+ mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+ SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
+ mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+};
+
+static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+ {"I05", NULL, "DL1"},
+ {"I06", NULL, "DL1"},
+ {"I2S Playback", NULL, "O03"},
+ {"I2S Playback", NULL, "O04"},
+ {"VUL", NULL, "O09"},
+ {"VUL", NULL, "O10"},
+ {"I17", NULL, "I2S Capture"},
+ {"I18", NULL, "I2S Capture"},
+ { "O03", "I05 Switch", "I05" },
+ { "O04", "I06 Switch", "I06" },
+ { "O09", "I17 Switch", "I17" },
+ { "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"},
+};
+
+static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
+ .name = "mtk-afe-pcm-dai",
+ .dapm_widgets = mtk_afe_pcm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
+ .dapm_routes = mtk_afe_pcm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
+};
+
+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),
+};
+
+static const char *aud_clks[MTK_CLK_NUM] = {
+ [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+ [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+ [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+ [MTK_CLK_I2S0_M] = "i2s0_m",
+ [MTK_CLK_I2S1_M] = "i2s1_m",
+ [MTK_CLK_I2S2_M] = "i2s2_m",
+ [MTK_CLK_I2S3_M] = "i2s3_m",
+ [MTK_CLK_I2S3_B] = "i2s3_b",
+ [MTK_CLK_BCK0] = "bck0",
+ [MTK_CLK_BCK1] = "bck1",
+};
+
+static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+ {
+ .name = "DL1",
+ .id = MTK_AFE_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .fs_shift = 0,
+ .mono_shift = 21,
+ .enable_shift = 1,
+ .irq_reg_cnt = AFE_IRQ_CNT1,
+ .irq_cnt_shift = 0,
+ .irq_en_shift = 0,
+ .irq_fs_shift = 4,
+ .irq_clr_shift = 0,
+ }, {
+ .name = "DL2",
+ .id = MTK_AFE_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .fs_shift = 4,
+ .mono_shift = 22,
+ .enable_shift = 2,
+ .irq_reg_cnt = AFE_IRQ_CNT1,
+ .irq_cnt_shift = 20,
+ .irq_en_shift = 2,
+ .irq_fs_shift = 16,
+ .irq_clr_shift = 2,
+ }, {
+ .name = "VUL",
+ .id = MTK_AFE_MEMIF_VUL,
+ .reg_ofs_base = AFE_VUL_BASE,
+ .reg_ofs_cur = AFE_VUL_CUR,
+ .fs_shift = 16,
+ .mono_shift = 27,
+ .enable_shift = 3,
+ .irq_reg_cnt = AFE_IRQ_CNT2,
+ .irq_cnt_shift = 0,
+ .irq_en_shift = 1,
+ .irq_fs_shift = 8,
+ .irq_clr_shift = 1,
+ }, {
+ .name = "DAI",
+ .id = MTK_AFE_MEMIF_DAI,
+ .reg_ofs_base = AFE_DAI_BASE,
+ .reg_ofs_cur = AFE_DAI_CUR,
+ .fs_shift = 24,
+ .mono_shift = -1,
+ .enable_shift = 4,
+ .irq_reg_cnt = AFE_IRQ_CNT2,
+ .irq_cnt_shift = 20,
+ .irq_en_shift = 3,
+ .irq_fs_shift = 20,
+ .irq_clr_shift = 3,
+ }, {
+ .name = "AWB",
+ .id = MTK_AFE_MEMIF_AWB,
+ .reg_ofs_base = AFE_AWB_BASE,
+ .reg_ofs_cur = AFE_AWB_CUR,
+ .fs_shift = 12,
+ .mono_shift = 24,
+ .enable_shift = 6,
+ .irq_reg_cnt = AFE_IRQ_CNT7,
+ .irq_cnt_shift = 0,
+ .irq_en_shift = 14,
+ .irq_fs_shift = 24,
+ .irq_clr_shift = 6,
+ }, {
+ .name = "MOD_DAI",
+ .id = MTK_AFE_MEMIF_MOD_DAI,
+ .reg_ofs_base = AFE_MOD_PCM_BASE,
+ .reg_ofs_cur = AFE_MOD_PCM_CUR,
+ .fs_shift = 30,
+ .mono_shift = 30,
+ .enable_shift = 7,
+ .irq_reg_cnt = AFE_IRQ_CNT2,
+ .irq_cnt_shift = 20,
+ .irq_en_shift = 3,
+ .irq_fs_shift = 20,
+ .irq_clr_shift = 3,
+ }, {
+ .name = "HDMI",
+ .id = MTK_AFE_MEMIF_HDMI,
+ .reg_ofs_base = AFE_HDMI_OUT_BASE,
+ .reg_ofs_cur = AFE_HDMI_OUT_CUR,
+ .fs_shift = -1,
+ .mono_shift = -1,
+ .enable_shift = -1,
+ .irq_reg_cnt = AFE_IRQ_CNT5,
+ .irq_cnt_shift = 0,
+ .irq_en_shift = 12,
+ .irq_fs_shift = -1,
+ .irq_clr_shift = 4,
+ },
+};
+
+static const struct regmap_config mtk_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_ADDA2_TOP_CON0,
+ .cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+{
+ struct mtk_afe *afe = dev_id;
+ unsigned int reg_value, hw_ptr;
+ int i, ret;
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &reg_value);
+ if (ret) {
+ dev_err(afe->dev, "%s irq status err\n", __func__);
+ reg_value = AFE_IRQ_STATUS_BITS;
+ goto err_irq;
+ }
+
+ for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
+ struct mtk_afe_memif *memif = &afe->memif[i];
+
+ if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+ continue;
+
+ ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur,
+ &hw_ptr);
+ if (ret || hw_ptr == 0) {
+ dev_err(afe->dev, "%s hw_ptr err\n", __func__);
+ hw_ptr = memif->phys_buf_addr;
+ }
+ memif->hw_ptr = hw_ptr - memif->phys_buf_addr;
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+
+ return IRQ_HANDLED;
+}
+
+static int mtk_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dev);
+
+ /* disable AFE clk */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
+
+ clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+ clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
+ clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+ clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+ clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+ return 0;
+}
+
+static int mtk_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+ if (ret)
+ goto err_infra;
+
+ ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+ if (ret)
+ goto err_top_aud_bus;
+
+ ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+ if (ret)
+ goto err_top_aud;
+
+ ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+ if (ret)
+ goto err_bck0;
+
+ /* enable AFE clk */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0);
+
+ /* set O3/O4 16bits */
+ regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
+ AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0);
+
+ /* unmask all IRQs */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff);
+ return 0;
+
+err_bck0:
+ clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+err_top_aud:
+ clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+err_top_aud_bus:
+ clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+err_infra:
+ clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+ return ret;
+}
+
+static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe->clocks[i])) {
+ dev_err(afe->dev, "%s devm_clk_get %s fail\n",
+ __func__, aud_clks[i]);
+ return PTR_ERR(afe->clocks[i]);
+ }
+ }
+ clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
+ clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+ return 0;
+}
+
+static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ unsigned int irq_id;
+ struct mtk_afe *afe;
+ struct resource *res;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->dev = &pdev->dev;
+
+ irq_id = platform_get_irq(pdev, 0);
+ if (!irq_id) {
+ dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
+ return -ENXIO;
+ }
+ ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+ 0, "Afe_ISR_Handle", (void *)afe);
+ if (ret) {
+ dev_err(afe->dev, "could not request_irq\n");
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mtk_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ /* initial audio related clock */
+ ret = mtk_afe_init_audio_clk(afe);
+ if (ret) {
+ dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+ return ret;
+ }
+
+ for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+ afe->memif[i].data = &memif_data[i];
+
+ platform_set_drvdata(pdev, afe);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = mtk_afe_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+ if (ret)
+ goto err_pm_disable;
+
+ ret = snd_soc_register_component(&pdev->dev,
+ &mtk_afe_pcm_dai_component,
+ mtk_afe_pcm_dais,
+ ARRAY_SIZE(mtk_afe_pcm_dais));
+ if (ret)
+ goto err_platform;
+
+ ret = snd_soc_register_component(&pdev->dev,
+ &mtk_afe_hdmi_dai_component,
+ mtk_afe_hdmi_dais,
+ ARRAY_SIZE(mtk_afe_hdmi_dais));
+ if (ret)
+ goto err_comp;
+
+ dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+ return 0;
+
+err_comp:
+ snd_soc_unregister_component(&pdev->dev);
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id mtk_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8173-afe-pcm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mtk_afe_pm_ops = {
+ SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
+ NULL)
+};
+
+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,
+ },
+ .probe = mtk_afe_pcm_dev_probe,
+ .remove = mtk_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mtk_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 6768e4f7d7d0..30d0109703a9 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -100,12 +100,13 @@ config SND_OMAP_SOC_OMAP_TWL4030
config SND_OMAP_SOC_OMAP_ABE_TWL6040
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
- depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST)
+ depends on TWL6040_CORE && SND_OMAP_SOC
+ depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
select SND_OMAP_SOC_DMIC
select SND_OMAP_SOC_MCPDM
select SND_SOC_TWL6040
select SND_SOC_DMIC
- select COMMON_CLK_PALMAS if MFD_PALMAS
+ select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
help
Say Y if you want to add support for SoC audio on OMAP boards using
ABE and twl6040 codec. This driver currently supports:
diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c
index 3673ada43bfb..743131473056 100644
--- a/sound/soc/omap/omap-twl4030.c
+++ b/sound/soc/omap/omap-twl4030.c
@@ -159,9 +159,8 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = rtd->card;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = &card->dapm;
struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
int ret = 0;
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index c2ddf0fbfa28..3bebfb1d3a6f 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -245,6 +245,8 @@ static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
static const struct snd_soc_dapm_route audio_map[] = {
{"Ext Spk", NULL, "HPLOUT"},
{"Ext Spk", NULL, "HPROUT"},
+ {"Ext Spk", NULL, "HPLCOM"},
+ {"Ext Spk", NULL, "HPRCOM"},
{"Headphone Jack", NULL, "LLOUT"},
{"Headphone Jack", NULL, "RLOUT"},
{"FM Transmitter", NULL, "LLOUT"},
@@ -288,15 +290,8 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = rtd->card;
struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
-
- struct snd_soc_dapm_context *dapm = &codec->dapm;
int err;
- /* Set up NC codec pins */
- snd_soc_dapm_nc_pin(dapm, "MIC3L");
- snd_soc_dapm_nc_pin(dapm, "MIC3R");
- snd_soc_dapm_nc_pin(dapm, "LINE1R");
-
err = tpa6130a2_add_controls(codec);
if (err < 0) {
dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
@@ -383,6 +378,7 @@ static struct snd_soc_card rx51_sound_card = {
.num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
.codec_conf = rx51_codec_conf,
.num_configs = ARRAY_SIZE(rx51_codec_conf),
+ .fully_routed = true,
.controls = aic34_rx51_controls,
.num_controls = ARRAY_SIZE(aic34_rx51_controls),
@@ -455,50 +451,36 @@ static int rx51_soc_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, pdata);
pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
- "tvout-selection");
+ "tvout-selection",
+ GPIOD_OUT_LOW);
if (IS_ERR(pdata->tvout_selection_gpio)) {
dev_err(card->dev, "could not get tvout selection gpio\n");
return PTR_ERR(pdata->tvout_selection_gpio);
}
- err = gpiod_direction_output(pdata->tvout_selection_gpio, 0);
- if (err) {
- dev_err(card->dev, "could not setup tvout selection gpio\n");
- return err;
- }
-
pdata->jack_detection_gpio = devm_gpiod_get(card->dev,
- "jack-detection");
+ "jack-detection",
+ GPIOD_ASIS);
if (IS_ERR(pdata->jack_detection_gpio)) {
dev_err(card->dev, "could not get jack detection gpio\n");
return PTR_ERR(pdata->jack_detection_gpio);
}
- pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch");
+ pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch",
+ GPIOD_OUT_HIGH);
if (IS_ERR(pdata->eci_sw_gpio)) {
dev_err(card->dev, "could not get eci switch gpio\n");
return PTR_ERR(pdata->eci_sw_gpio);
}
- err = gpiod_direction_output(pdata->eci_sw_gpio, 1);
- if (err) {
- dev_err(card->dev, "could not setup eci switch gpio\n");
- return err;
- }
-
pdata->speaker_amp_gpio = devm_gpiod_get(card->dev,
- "speaker-amplifier");
+ "speaker-amplifier",
+ GPIOD_OUT_LOW);
if (IS_ERR(pdata->speaker_amp_gpio)) {
dev_err(card->dev, "could not get speaker enable gpio\n");
return PTR_ERR(pdata->speaker_amp_gpio);
}
- err = gpiod_direction_output(pdata->speaker_amp_gpio, 0);
- if (err) {
- dev_err(card->dev, "could not setup speaker enable gpio\n");
- return err;
- }
-
err = devm_snd_soc_register_card(card->dev, card);
if (err) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err);
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index 79936e3e80e7..2b26318bc200 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = {
{"MICBIAS1", NULL, "Main Mic"},
};
-static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- /* set endpoints to not connected */
- snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
- snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
- snd_soc_dapm_nc_pin(dapm, "IN1LN");
- snd_soc_dapm_nc_pin(dapm, "IN1LP");
- snd_soc_dapm_nc_pin(dapm, "IN1RP");
- snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
- snd_soc_dapm_nc_pin(dapm, "IN2RN");
- snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
- snd_soc_dapm_nc_pin(dapm, "IN2LN");
-
- return 0;
-}
-
static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -115,7 +92,6 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &brownstone_ops,
- .init = brownstone_wm8994_init,
},
};
@@ -132,6 +108,7 @@ static struct snd_soc_card brownstone = {
.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
.dapm_routes = brownstone_audio_map,
.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
+ .fully_routed = true,
};
static int brownstone_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 0fce8c420e96..80b457ac522a 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -192,6 +192,7 @@ static int poodle_amp_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
+SND_SOC_DAPM_MIC("Microphone", NULL),
};
/* Corgi machine connections to the codec pins */
@@ -204,6 +205,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = {
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},
{"Ext Spk", NULL, "LOUT"},
+
+ {"MICIN", NULL, "Microphone"},
};
static const char *jack_function[] = {"Off", "Headphone"};
@@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
poodle_set_spk),
};
-/*
- * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
- */
-static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_nc_pin(dapm, "LLINEIN");
- snd_soc_dapm_nc_pin(dapm, "RLINEIN");
-
- return 0;
-}
-
/* poodle digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link poodle_dai = {
.name = "WM8731",
@@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = {
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm8731.0-001b",
- .init = poodle_wm8731_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &poodle_ops,
@@ -261,6 +249,7 @@ static struct snd_soc_card poodle = {
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
.dapm_routes = poodle_audio_map,
.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
+ .fully_routed = true,
};
static int poodle_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index cb49284e853a..f59f566551ef 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = {
tosa_set_spk),
};
-static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_nc_pin(dapm, "OUT3");
- snd_soc_dapm_nc_pin(dapm, "MONOOUT");
-
- return 0;
-}
-
static struct snd_soc_dai_link tosa_dai[] = {
{
.name = "AC97",
@@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = {
.codec_dai_name = "wm9712-hifi",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm9712-codec",
- .init = tosa_ac97_init,
.ops = &tosa_ops,
},
{
@@ -230,6 +218,7 @@ static struct snd_soc_card tosa = {
.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
.dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
};
static int tosa_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c
index bcbfbe8303f7..990b1aa6d7f6 100644
--- a/sound/soc/pxa/z2.c
+++ b/sound/soc/pxa/z2.c
@@ -132,16 +132,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
*/
static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
- /* NC codec pins */
- snd_soc_dapm_disable_pin(dapm, "LINPUT3");
- snd_soc_dapm_disable_pin(dapm, "RINPUT3");
- snd_soc_dapm_disable_pin(dapm, "OUT3");
- snd_soc_dapm_disable_pin(dapm, "MONO1");
-
/* Jack detection API stuff */
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
&hs_jack, hs_jack_pins,
@@ -189,6 +181,7 @@ static struct snd_soc_card snd_soc_z2 = {
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
.dapm_routes = z2_audio_map,
.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
+ .fully_routed = true,
};
static struct platform_device *z2_snd_device;
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 5f58e4f1bca9..807fedfa1c76 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -6,20 +6,38 @@ config SND_SOC_QCOM
config SND_SOC_LPASS_CPU
tristate
- depends on SND_SOC_QCOM
select REGMAP_MMIO
config SND_SOC_LPASS_PLATFORM
tristate
- depends on SND_SOC_QCOM
select REGMAP_MMIO
-config SND_SOC_STORM
- tristate "ASoC I2S support for Storm boards"
- depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
+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)
+ select SND_SOC_LPASS_IPQ806X
select SND_SOC_MAX98357A
help
Say Y or M if you want add support for SoC audio on the
Qualcomm Technologies IPQ806X-based Storm board.
+
+config SND_SOC_APQ8016_SBC
+ tristate "SoC Audio support for APQ8016 SBC platforms"
+ depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
+ select SND_SOC_LPASS_APQ8016
+ help
+ Support for Qualcomm Technologies LPASS audio block in
+ APQ8016 SOC-based systems.
+ Say Y if you want to use audio devices on MI2S.
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index c5ce96c761c4..79e5c50a8f71 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -1,11 +1,17 @@
# Platform
snd-soc-lpass-cpu-objs := lpass-cpu.o
snd-soc-lpass-platform-objs := lpass-platform.o
+snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
+snd-soc-lpass-apq8016-objs := lpass-apq8016.o
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
+obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
+obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
# Machine
snd-soc-storm-objs := storm.o
+snd-soc-apq8016-sbc-objs := apq8016_sbc.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
+obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
new file mode 100644
index 000000000000..1efdf0088ecd
--- /dev/null
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <dt-bindings/sound/apq8016-lpass.h>
+
+struct apq8016_sbc_data {
+ void __iomem *mic_iomux;
+ void __iomem *spkr_iomux;
+ struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
+};
+
+#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17)
+#define MIC_CTRL_TLMM_SCLK_EN BIT(1)
+#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16))
+
+static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card);
+ int rval = 0;
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
+ pdata->spkr_iomux);
+ break;
+
+ case MI2S_QUATERNARY:
+ /* Configure the Quat MI2S to TLMM */
+ writel(readl(pdata->mic_iomux) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 |
+ MIC_CTRL_TLMM_SCLK_EN,
+ pdata->mic_iomux);
+ break;
+
+ default:
+ dev_err(card->dev, "unsupported cpu dai configuration\n");
+ rval = -EINVAL;
+ break;
+
+ }
+
+ return rval;
+}
+
+static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *link;
+ struct device_node *np, *codec, *cpu, *node = dev->of_node;
+ struct apq8016_sbc_data *data;
+ int ret, num_links;
+
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(dev, "Error parsing card name: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ /* Populate links */
+ num_links = of_get_child_count(node);
+
+ /* Allocate the private data and the DAI link array */
+ data = devm_kzalloc(dev, sizeof(*data) + sizeof(*link) * num_links,
+ GFP_KERNEL);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ card->dai_link = &data->dai_link[0];
+ card->num_links = num_links;
+
+ link = data->dai_link;
+
+ for_each_child_of_node(node, np) {
+ cpu = of_get_child_by_name(np, "cpu");
+ codec = of_get_child_by_name(np, "codec");
+
+ if (!cpu || !codec) {
+ dev_err(dev, "Can't find cpu/codec DT node\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
+ if (!link->cpu_of_node) {
+ dev_err(card->dev, "error getting cpu phandle\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0);
+ if (!link->codec_of_node) {
+ dev_err(card->dev, "error getting codec phandle\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name);
+ if (ret) {
+ dev_err(card->dev, "error getting cpu dai name\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name);
+ if (ret) {
+ dev_err(card->dev, "error getting codec dai name\n");
+ return ERR_PTR(ret);
+ }
+
+ link->platform_of_node = link->cpu_of_node;
+ /* For now we only support playback */
+ link->playback_only = true;
+
+ ret = of_property_read_string(np, "link-name", &link->name);
+ if (ret) {
+ dev_err(card->dev, "error getting codec dai_link name\n");
+ return ERR_PTR(ret);
+ }
+
+ link->stream_name = link->name;
+ link->init = apq8016_sbc_dai_init;
+ link++;
+ }
+
+ return data;
+}
+
+static int apq8016_sbc_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ struct apq8016_sbc_data *data;
+ struct resource *res;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ data = apq8016_sbc_parse_of(card);
+ if (IS_ERR(data)) {
+ dev_err(&pdev->dev, "Error resolving dai links: %ld\n",
+ PTR_ERR(data));
+ return PTR_ERR(data);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux");
+ data->mic_iomux = devm_ioremap_resource(dev, res);
+ if (IS_ERR(data->mic_iomux))
+ return PTR_ERR(data->mic_iomux);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux");
+ data->spkr_iomux = devm_ioremap_resource(dev, res);
+ if (IS_ERR(data->spkr_iomux))
+ return PTR_ERR(data->spkr_iomux);
+
+ platform_set_drvdata(pdev, data);
+ snd_soc_card_set_drvdata(card, data);
+
+ return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static const struct of_device_id apq8016_sbc_device_id[] = {
+ { .compatible = "qcom,apq8016-sbc-sndcard" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id);
+
+static struct platform_driver apq8016_sbc_platform_driver = {
+ .driver = {
+ .name = "qcom-apq8016-sbc",
+ .of_match_table = of_match_ptr(apq8016_sbc_device_id),
+ },
+ .probe = apq8016_sbc_platform_probe,
+};
+module_platform_driver(apq8016_sbc_platform_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
new file mode 100644
index 000000000000..94efc01020c4
--- /dev/null
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
+ *
+ */
+
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/apq8016-lpass.h>
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
+ [MI2S_PRIMARY] = {
+ .id = MI2S_PRIMARY,
+ .name = "Primary MI2S",
+ .playback = {
+ .stream_name = "Primary Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .probe = &asoc_qcom_lpass_cpu_dai_probe,
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ },
+ [MI2S_SECONDARY] = {
+ .id = MI2S_SECONDARY,
+ .name = "Secondary MI2S",
+ .playback = {
+ .stream_name = "Secondary Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .probe = &asoc_qcom_lpass_cpu_dai_probe,
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ },
+ [MI2S_TERTIARY] = {
+ .id = MI2S_TERTIARY,
+ .name = "Tertiary MI2S",
+ .capture = {
+ .stream_name = "Tertiary Capture",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .probe = &asoc_qcom_lpass_cpu_dai_probe,
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ },
+ [MI2S_QUATERNARY] = {
+ .id = MI2S_QUATERNARY,
+ .name = "Quatenary MI2S",
+ .playback = {
+ .stream_name = "Quatenary Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .stream_name = "Quatenary Capture",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .probe = &asoc_qcom_lpass_cpu_dai_probe,
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ },
+};
+
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+ struct lpass_variant *v = drvdata->variant;
+ int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+ v->rdma_channels);
+
+ if (chan >= v->rdma_channels)
+ return -EBUSY;
+
+ set_bit(chan, &drvdata->rdma_ch_bit_map);
+
+ return chan;
+}
+
+static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+ clear_bit(chan, &drvdata->rdma_ch_bit_map);
+
+ return 0;
+}
+
+static int apq8016_lpass_init(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");
+ if (IS_ERR(drvdata->pcnoc_mport_clk)) {
+ dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n",
+ __func__, PTR_ERR(drvdata->pcnoc_mport_clk));
+ return PTR_ERR(drvdata->pcnoc_mport_clk);
+ }
+
+ ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");
+ if (IS_ERR(drvdata->pcnoc_sway_clk)) {
+ dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n",
+ __func__, PTR_ERR(drvdata->pcnoc_sway_clk));
+ return PTR_ERR(drvdata->pcnoc_sway_clk);
+ }
+
+ ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int apq8016_lpass_exit(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(drvdata->pcnoc_mport_clk);
+ clk_disable_unprepare(drvdata->pcnoc_sway_clk);
+
+ return 0;
+}
+
+
+static struct lpass_variant apq8016_data = {
+ .i2sctrl_reg_base = 0x1000,
+ .i2sctrl_reg_stride = 0x1000,
+ .i2s_ports = 4,
+ .irq_reg_base = 0x6000,
+ .irq_reg_stride = 0x1000,
+ .irq_ports = 3,
+ .rdma_reg_base = 0x8400,
+ .rdma_reg_stride = 0x1000,
+ .rdma_channels = 2,
+ .rdmactl_audif_start = 1,
+ .dai_driver = apq8016_lpass_cpu_dai_driver,
+ .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+ .init = apq8016_lpass_init,
+ .exit = apq8016_lpass_exit,
+ .alloc_dma_channel = apq8016_lpass_alloc_dma_channel,
+ .free_dma_channel = apq8016_lpass_free_dma_channel,
+};
+
+static const struct of_device_id apq8016_lpass_cpu_device_id[] = {
+ { .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
+
+static struct platform_driver apq8016_lpass_cpu_platform_driver = {
+ .driver = {
+ .name = "apq8016-lpass-cpu",
+ .of_match_table = of_match_ptr(apq8016_lpass_cpu_device_id),
+ },
+ .probe = asoc_qcom_lpass_cpu_platform_probe,
+ .remove = asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(apq8016_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index dc790abaa331..23f3d59e6d09 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -14,21 +14,17 @@
*/
#include <linux/clk.h>
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
#include "lpass.h"
static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
- ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
+ if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+ return 0;
+
+ ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
if (ret)
dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
__func__, freq, ret);
@@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
- ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
- if (ret) {
- dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
- __func__, ret);
- return ret;
+ if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
+ ret = clk_prepare_enable(
+ drvdata->mi2s_osr_clk[dai->driver->id]);
+ if (ret) {
+ dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
+ __func__, ret);
+ return ret;
+ }
}
- ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
+ ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
if (ret) {
dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
__func__, ret);
- clk_disable_unprepare(drvdata->mi2s_osr_clk);
+ if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+ clk_disable_unprepare(
+ drvdata->mi2s_osr_clk[dai->driver->id]);
return ret;
}
@@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
- clk_disable_unprepare(drvdata->mi2s_bit_clk);
- clk_disable_unprepare(drvdata->mi2s_osr_clk);
+ clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+
+ if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+ clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
}
static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
@@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
+ LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+ regval);
if (ret) {
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
return ret;
}
- ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
+ ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
+ rate * bitwidth * 2);
if (ret) {
dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
__func__, rate * bitwidth * 2, ret);
@@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
int ret;
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+ LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+ 0);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
@@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
int ret;
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+ LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
@@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+ LPAIF_I2SCTL_REG(drvdata->variant,
+ dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK,
LPAIF_I2SCTL_SPKEN_ENABLE);
if (ret)
@@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+ LPAIF_I2SCTL_REG(drvdata->variant,
+ dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK,
LPAIF_I2SCTL_SPKEN_DISABLE);
if (ret)
@@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
+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,
@@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
.prepare = lpass_cpu_daiops_prepare,
.trigger = lpass_cpu_daiops_trigger,
};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
-static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+ LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
return ret;
}
-
-static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
- .playback = {
- .stream_name = "lpass-cpu-playback",
- .formats = SNDRV_PCM_FMTBIT_S16 |
- SNDRV_PCM_FMTBIT_S24 |
- SNDRV_PCM_FMTBIT_S32,
- .rates = SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000,
- .rate_min = 8000,
- .rate_max = 96000,
- .channels_min = 1,
- .channels_max = 8,
- },
- .probe = &lpass_cpu_dai_probe,
- .ops = &lpass_cpu_dai_ops,
-};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
.name = "lpass-cpu",
@@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ struct lpass_variant *v = drvdata->variant;
int i;
- for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
- if (reg == LPAIF_I2SCTL_REG(i))
+ for (i = 0; i < v->i2s_ports; ++i)
+ if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
- for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
- if (reg == LPAIF_IRQEN_REG(i))
+ for (i = 0; i < v->irq_ports; ++i) {
+ if (reg == LPAIF_IRQEN_REG(v, i))
return true;
- if (reg == LPAIF_IRQCLEAR_REG(i))
+ if (reg == LPAIF_IRQCLEAR_REG(v, i))
return true;
}
- for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
- if (reg == LPAIF_RDMACTL_REG(i))
+ for (i = 0; i < v->rdma_channels; ++i) {
+ if (reg == LPAIF_RDMACTL_REG(v, i))
return true;
- if (reg == LPAIF_RDMABASE_REG(i))
+ if (reg == LPAIF_RDMABASE_REG(v, i))
return true;
- if (reg == LPAIF_RDMABUFF_REG(i))
+ if (reg == LPAIF_RDMABUFF_REG(v, i))
return true;
- if (reg == LPAIF_RDMAPER_REG(i))
+ if (reg == LPAIF_RDMAPER_REG(v, i))
return true;
}
@@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ struct lpass_variant *v = drvdata->variant;
int i;
- for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
- if (reg == LPAIF_I2SCTL_REG(i))
+ for (i = 0; i < v->i2s_ports; ++i)
+ if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
- for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
- if (reg == LPAIF_IRQEN_REG(i))
+ for (i = 0; i < v->irq_ports; ++i) {
+ if (reg == LPAIF_IRQEN_REG(v, i))
return true;
- if (reg == LPAIF_IRQSTAT_REG(i))
+ if (reg == LPAIF_IRQSTAT_REG(v, i))
return true;
}
- for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
- if (reg == LPAIF_RDMACTL_REG(i))
+ for (i = 0; i < v->rdma_channels; ++i) {
+ if (reg == LPAIF_RDMACTL_REG(v, i))
return true;
- if (reg == LPAIF_RDMABASE_REG(i))
+ if (reg == LPAIF_RDMABASE_REG(v, i))
return true;
- if (reg == LPAIF_RDMABUFF_REG(i))
+ if (reg == LPAIF_RDMABUFF_REG(v, i))
return true;
- if (reg == LPAIF_RDMACURR_REG(i))
+ if (reg == LPAIF_RDMACURR_REG(v, i))
return true;
- if (reg == LPAIF_RDMAPER_REG(i))
+ if (reg == LPAIF_RDMAPER_REG(v, i))
return true;
}
@@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ struct lpass_variant *v = drvdata->variant;
int i;
- for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
- if (reg == LPAIF_IRQSTAT_REG(i))
+ for (i = 0; i < v->irq_ports; ++i)
+ if (reg == LPAIF_IRQSTAT_REG(v, i))
return true;
- for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
- if (reg == LPAIF_RDMACURR_REG(i))
+ for (i = 0; i < v->rdma_channels; ++i)
+ if (reg == LPAIF_RDMACURR_REG(v, i))
return true;
return false;
}
-static const struct regmap_config lpass_cpu_regmap_config = {
+static struct regmap_config lpass_cpu_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
.writeable_reg = lpass_cpu_regmap_writeable,
.readable_reg = lpass_cpu_regmap_readable,
.volatile_reg = lpass_cpu_regmap_volatile,
.cache_type = REGCACHE_FLAT,
};
-static int lpass_cpu_platform_probe(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
{
struct lpass_data *drvdata;
struct device_node *dsp_of_node;
struct resource *res;
- int ret;
+ struct lpass_variant *variant;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ char clk_name[16];
+ int ret, i, dai_id;
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
if (dsp_of_node) {
@@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, drvdata);
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data)
+ return -EINVAL;
+
+ drvdata->variant = (struct lpass_variant *)match->data;
+ variant = drvdata->variant;
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
- if (!res) {
- dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
- return -ENODEV;
- }
drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR((void const __force *)drvdata->lpaif)) {
@@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR((void const __force *)drvdata->lpaif);
}
+ lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
+ variant->rdma_channels);
+
drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
&lpass_cpu_regmap_config);
if (IS_ERR(drvdata->lpaif_map)) {
@@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR(drvdata->lpaif_map);
}
- drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
- if (IS_ERR(drvdata->mi2s_osr_clk)) {
- dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
- __func__, PTR_ERR(drvdata->mi2s_osr_clk));
- return PTR_ERR(drvdata->mi2s_osr_clk);
- }
-
- drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
- if (IS_ERR(drvdata->mi2s_bit_clk)) {
- dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
- __func__, PTR_ERR(drvdata->mi2s_bit_clk));
- return PTR_ERR(drvdata->mi2s_bit_clk);
+ if (variant->init)
+ variant->init(pdev);
+
+ for (i = 0; i < variant->num_dai; i++) {
+ dai_id = variant->dai_driver[i].id;
+ if (variant->num_dai > 1)
+ sprintf(clk_name, "mi2s-osr-clk%d", i);
+ else
+ sprintf(clk_name, "mi2s-osr-clk");
+
+ drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
+ clk_name);
+ if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
+ dev_warn(&pdev->dev,
+ "%s() error getting mi2s-osr-clk: %ld\n",
+ __func__,
+ PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
+ }
+
+ if (variant->num_dai > 1)
+ sprintf(clk_name, "mi2s-bit-clk%d", i);
+ else
+ sprintf(clk_name, "mi2s-bit-clk");
+
+ drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
+ clk_name);
+ if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
+ dev_err(&pdev->dev,
+ "%s() error getting mi2s-bit-clk: %ld\n",
+ __func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+ return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
+ }
}
drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
@@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_component(&pdev->dev,
- &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
+ &lpass_cpu_comp_driver,
+ variant->dai_driver,
+ variant->num_dai);
if (ret) {
dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
__func__, ret);
@@ -459,33 +489,17 @@ err_clk:
clk_disable_unprepare(drvdata->ahbix_clk);
return ret;
}
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
-static int lpass_cpu_platform_remove(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ if (drvdata->variant->exit)
+ drvdata->variant->exit(pdev);
+
clk_disable_unprepare(drvdata->ahbix_clk);
return 0;
}
-
-#ifdef CONFIG_OF
-static const struct of_device_id lpass_cpu_device_id[] = {
- { .compatible = "qcom,lpass-cpu" },
- {}
-};
-MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
-#endif
-
-static struct platform_driver lpass_cpu_platform_driver = {
- .driver = {
- .name = "lpass-cpu",
- .of_match_table = of_match_ptr(lpass_cpu_device_id),
- },
- .probe = lpass_cpu_platform_probe,
- .remove = lpass_cpu_platform_remove,
-};
-module_platform_driver(lpass_cpu_platform_driver);
-
-MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
new file mode 100644
index 000000000000..7356d3a766d6
--- /dev/null
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
+ * Splited out the IPQ8064 soc specific from lpass-cpu.c
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+enum lpaif_i2s_ports {
+ IPQ806X_LPAIF_I2S_PORT_CODEC_SPK,
+ IPQ806X_LPAIF_I2S_PORT_CODEC_MIC,
+ IPQ806X_LPAIF_I2S_PORT_SEC_SPK,
+ IPQ806X_LPAIF_I2S_PORT_SEC_MIC,
+ IPQ806X_LPAIF_I2S_PORT_MI2S,
+};
+
+enum lpaif_dma_channels {
+ IPQ806X_LPAIF_RDMA_CHAN_MI2S,
+ IPQ806X_LPAIF_RDMA_CHAN_PCM0,
+ IPQ806X_LPAIF_RDMA_CHAN_PCM1,
+};
+
+static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
+ .id = IPQ806X_LPAIF_I2S_PORT_MI2S,
+ .playback = {
+ .stream_name = "lpass-cpu-playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .probe = &asoc_qcom_lpass_cpu_dai_probe,
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+};
+
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+ return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+}
+
+static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+ return 0;
+}
+
+struct lpass_variant ipq806x_data = {
+ .i2sctrl_reg_base = 0x0010,
+ .i2sctrl_reg_stride = 0x04,
+ .i2s_ports = 5,
+ .irq_reg_base = 0x3000,
+ .irq_reg_stride = 0x1000,
+ .irq_ports = 3,
+ .rdma_reg_base = 0x6000,
+ .rdma_reg_stride = 0x1000,
+ .rdma_channels = 4,
+ .dai_driver = &ipq806x_lpass_cpu_dai_driver,
+ .num_dai = 1,
+ .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
+ .free_dma_channel = ipq806x_lpass_free_dma_channel,
+};
+
+static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
+ { .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id);
+
+static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
+ .driver = {
+ .name = "lpass-cpu",
+ .of_match_table = of_match_ptr(ipq806x_lpass_cpu_device_id),
+ },
+ .probe = asoc_qcom_lpass_cpu_platform_probe,
+ .remove = asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(ipq806x_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-reg.h
index dc423b888842..95e22f131052 100644
--- a/sound/soc/qcom/lpass-lpaif-ipq806x.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -9,37 +9,17 @@
* 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.
- *
- * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
*/
-#ifndef __LPASS_LPAIF_H__
-#define __LPASS_LPAIF_H__
-
-#define LPAIF_BANK_OFFSET 0x1000
+#ifndef __LPASS_LPAIF_REG_H__
+#define __LPASS_LPAIF_REG_H__
/* LPAIF I2S */
-#define LPAIF_I2SCTL_REG_BASE 0x0010
-#define LPAIF_I2SCTL_REG_STRIDE 0x4
-#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
- (LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
-
-enum lpaif_i2s_ports {
- LPAIF_I2S_PORT_MIN = 0,
-
- LPAIF_I2S_PORT_CODEC_SPK = 0,
- LPAIF_I2S_PORT_CODEC_MIC = 1,
- LPAIF_I2S_PORT_SEC_SPK = 2,
- LPAIF_I2S_PORT_SEC_MIC = 3,
- LPAIF_I2S_PORT_MI2S = 4,
-
- LPAIF_I2S_PORT_MAX = 4,
- LPAIF_I2S_PORT_NUM = 5,
-};
-
-#define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port))
+#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
+ (v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
+#define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000
#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15
#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
@@ -79,55 +59,36 @@ enum lpaif_i2s_ports {
#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
/* LPAIF IRQ */
+#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
+ (v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
-#define LPAIF_IRQ_REG_BASE 0x3000
-#define LPAIF_IRQ_REG_STRIDE 0x1000
-#define LPAIF_IRQ_REG_ADDR(addr, port) \
- (LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
-
-enum lpaif_irq_ports {
- LPAIF_IRQ_PORT_MIN = 0,
+#define LPAIF_IRQ_PORT_HOST 0
- LPAIF_IRQ_PORT_HOST = 0,
- LPAIF_IRQ_PORT_ADSP = 1,
-
- LPAIF_IRQ_PORT_MAX = 2,
- LPAIF_IRQ_PORT_NUM = 3,
-};
-
-#define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port))
-#define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port))
-#define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port))
+#define LPAIF_IRQEN_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
+#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
+#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
#define LPAIF_IRQ_BITSTRIDE 3
+
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
/* LPAIF DMA */
-#define LPAIF_RDMA_REG_BASE 0x6000
-#define LPAIF_RDMA_REG_STRIDE 0x1000
-#define LPAIF_RDMA_REG_ADDR(addr, chan) \
- (LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
-
-enum lpaif_dma_channels {
- LPAIF_RDMA_CHAN_MIN = 0,
-
- LPAIF_RDMA_CHAN_MI2S = 0,
- LPAIF_RDMA_CHAN_PCM0 = 1,
- LPAIF_RDMA_CHAN_PCM1 = 2,
+#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
+ (v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
- LPAIF_RDMA_CHAN_MAX = 4,
- LPAIF_RDMA_CHAN_NUM = 5,
-};
+#define LPAIF_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan))
-#define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan))
-#define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan))
-#define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan))
-#define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan))
+#define LPAIF_RDMACTL_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_RDMABASE_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
+#define LPAIF_RDMABUFF_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_RDMACURR_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan))
+#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
+#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMACTL_BURSTEN_MASK 0x800
#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
@@ -145,13 +106,6 @@ enum lpaif_dma_channels {
#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
-#define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E
#define LPAIF_RDMACTL_FIFOWM_SHIFT 1
@@ -169,4 +123,4 @@ enum lpaif_dma_channels {
#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
-#endif /* __LPASS_LPAIF_H__ */
+#endif /* __LPASS_LPAIF_REG_H__ */
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 2fa6280dfb23..79688aa1941a 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -13,23 +13,22 @@
* lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
*/
-#include <linux/compiler.h>
-#include <linux/device.h>
#include <linux/dma-mapping.h>
-#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/io.h>
#include <linux/platform_device.h>
-#include <sound/memalloc.h>
-#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
#include <sound/soc.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
#include "lpass.h"
+struct lpass_pcm_data {
+ int rdma_ch;
+ int i2s_port;
+};
+
#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
#define LPASS_PLATFORM_PERIODS 2
@@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_variant *v = drvdata->variant;
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
unsigned int regval;
int bitwidth;
- int ret;
+ int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
@@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
}
regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
- LPAIF_RDMACTL_AUDINTF_MI2S |
+ LPAIF_RDMACTL_AUDINTF(rdma_port) |
LPAIF_RDMACTL_FIFOWM_8;
switch (bitwidth) {
@@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
+ LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_variant *v = drvdata->variant;
int ret;
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+ LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
if (ret)
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- int ret;
+ struct lpass_variant *v = drvdata->variant;
+ int ret, ch = pcm_data->rdma_ch;
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
+ LPAIF_RDMABASE_REG(v, ch),
runtime->dma_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
+ LPAIF_RDMABUFF_REG(v, ch),
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
+ LPAIF_RDMAPER_REG(v, ch),
(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+ LPAIF_RDMACTL_REG(v, ch),
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
@@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- int ret;
+ struct lpass_variant *v = drvdata->variant;
+ int ret, ch = pcm_data->rdma_ch;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* clear status before enabling interrupts */
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+ LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+ LPAIF_IRQ_ALL(ch));
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, ret);
@@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
- LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+ LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+ LPAIF_IRQ_ALL(ch),
+ LPAIF_IRQ_ALL(ch));
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
@@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+ LPAIF_RDMACTL_REG(v, ch),
LPAIF_RDMACTL_ENABLE_MASK,
LPAIF_RDMACTL_ENABLE_ON);
if (ret) {
@@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+ LPAIF_RDMACTL_REG(v, ch),
LPAIF_RDMACTL_ENABLE_MASK,
LPAIF_RDMACTL_ENABLE_OFF);
if (ret) {
@@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
+ LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+ LPAIF_IRQ_ALL(ch), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
@@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr;
- int ret;
+ int ret, ch = pcm_data->rdma_ch;
ret = regmap_read(drvdata->lpaif_map,
- LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
+ LPAIF_RDMABASE_REG(v, ch), &base_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
__func__, ret);
@@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
}
ret = regmap_read(drvdata->lpaif_map,
- LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
+ LPAIF_RDMACURR_REG(v, ch), &curr_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
__func__, ret);
@@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
.mmap = lpass_platform_pcmops_mmap,
};
-static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+static irqreturn_t lpass_dma_interrupt_handler(
+ struct snd_pcm_substream *substream,
+ struct lpass_data *drvdata,
+ int chan, u32 interrupts)
{
- struct snd_pcm_substream *substream = data;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- unsigned int interrupts;
+ struct lpass_variant *v = drvdata->variant;
irqreturn_t ret = IRQ_NONE;
int rv;
- rv = regmap_read(drvdata->lpaif_map,
- LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
- if (rv) {
- dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
- __func__, rv);
- return IRQ_NONE;
- }
- interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
-
- if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
+ if (interrupts & LPAIF_IRQ_PER(chan)) {
rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
+ LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+ LPAIF_IRQ_PER(chan));
if (rv) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, rv);
@@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
ret = IRQ_HANDLED;
}
- if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
+ if (interrupts & LPAIF_IRQ_XRUN(chan)) {
rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
+ LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+ LPAIF_IRQ_XRUN(chan));
if (rv) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, rv);
@@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
ret = IRQ_HANDLED;
}
- if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
+ if (interrupts & LPAIF_IRQ_ERR(chan)) {
rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
+ LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+ LPAIF_IRQ_ERR(chan));
if (rv) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, rv);
@@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return ret;
}
+static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+{
+ struct lpass_data *drvdata = data;
+ struct lpass_variant *v = drvdata->variant;
+ unsigned int irqs;
+ int rv, chan;
+
+ rv = regmap_read(drvdata->lpaif_map,
+ LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+ if (rv) {
+ pr_err("%s() error reading from irqstat reg: %d\n",
+ __func__, rv);
+ return IRQ_NONE;
+ }
+
+ /* Handle per channel interrupts */
+ for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
+ if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
+ rv = lpass_dma_interrupt_handler(
+ drvdata->substream[chan],
+ drvdata, chan, irqs);
+ if (rv != IRQ_HANDLED)
+ return rv;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *soc_runtime)
{
@@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
struct snd_pcm *pcm = soc_runtime->pcm;
struct snd_pcm_substream *substream =
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_variant *v = drvdata->variant;
int ret;
+ struct lpass_pcm_data *data;
+
+ data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (v->alloc_dma_channel)
+ data->rdma_ch = v->alloc_dma_channel(drvdata);
+
+ if (IS_ERR_VALUE(data->rdma_ch))
+ return data->rdma_ch;
+
+ drvdata->substream[data->rdma_ch] = substream;
+ data->i2s_port = cpu_dai->driver->id;
+
+ snd_soc_pcm_set_drvdata(soc_runtime, data);
soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
@@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
if (ret)
return ret;
- ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
- lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
- "lpass-irq-lpaif", substream);
- if (ret) {
- dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
- __func__, ret);
- goto err_buf;
- }
-
- /* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
- if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
- __func__, ret);
- return ret;
- }
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+ LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
- return ret;
+ goto err_buf;
}
return 0;
@@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
struct snd_pcm_substream *substream =
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_data *drvdata =
+ snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
+ struct lpass_variant *v = drvdata->variant;
+
+ drvdata->substream[data->rdma_ch] = NULL;
+
+ if (v->free_dma_channel)
+ v->free_dma_channel(drvdata, data->rdma_ch);
lpass_platform_free_buffer(substream, soc_runtime);
}
@@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ struct lpass_variant *v = drvdata->variant;
+ int ret;
drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
if (drvdata->lpaif_irq < 0) {
@@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
return -ENODEV;
}
+ /* ensure audio hardware is disabled */
+ ret = regmap_write(drvdata->lpaif_map,
+ LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
+ if (ret) {
+ dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
+ lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
+ "lpass-irq-lpaif", drvdata);
+ if (ret) {
+ dev_err(&pdev->dev, "%s() irq request failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+
return devm_snd_soc_register_platform(&pdev->dev,
&lpass_platform_driver);
}
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 5c99b3dace86..d6e86c119e74 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -22,6 +22,8 @@
#include <linux/regmap.h>
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
+#define LPASS_MAX_MI2S_PORTS (8)
+#define LPASS_MAX_DMA_CHANNELS (8)
/* Both the CPU DAI and platform drivers will access this data */
struct lpass_data {
@@ -30,10 +32,10 @@ struct lpass_data {
struct clk *ahbix_clk;
/* MI2S system clock */
- struct clk *mi2s_osr_clk;
+ struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS];
/* MI2S bit clock (derived from system clock by a divider */
- struct clk *mi2s_bit_clk;
+ struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
/* low-power audio interface (LPAIF) registers */
void __iomem *lpaif;
@@ -43,9 +45,54 @@ struct lpass_data {
/* interrupts from the low-power audio interface (LPAIF) */
int lpaif_irq;
+
+ /* SOC specific variations in the LPASS IP integration */
+ struct lpass_variant *variant;
+
+ /* bit map to keep track of static channel allocations */
+ unsigned long rdma_ch_bit_map;
+
+ /* used it for handling interrupt per dma channel */
+ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
+
+ /* 8016 specific */
+ struct clk *pcnoc_mport_clk;
+ struct clk *pcnoc_sway_clk;
+};
+
+/* Vairant data per each SOC */
+struct lpass_variant {
+ u32 i2sctrl_reg_base;
+ u32 i2sctrl_reg_stride;
+ u32 i2s_ports;
+ u32 irq_reg_base;
+ u32 irq_reg_stride;
+ u32 irq_ports;
+ u32 rdma_reg_base;
+ u32 rdma_reg_stride;
+ u32 rdma_channels;
+
+ /**
+ * on SOCs like APQ8016 the channel control bits start
+ * at different offset to ipq806x
+ **/
+ u32 rdmactl_audif_start;
+ /* SOC specific intialization like clocks */
+ int (*init)(struct platform_device *pdev);
+ int (*exit)(struct platform_device *pdev);
+ int (*alloc_dma_channel)(struct lpass_data *data);
+ int (*free_dma_channel)(struct lpass_data *data, int ch);
+
+ /* SOC specific dais */
+ struct snd_soc_dai_driver *dai_driver;
+ int num_dai;
};
/* register the platform driver from the CPU DAI driver */
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;
#endif /* __LPASS_H__ */
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c
index b8bd296190ad..2d833bffdba0 100644
--- a/sound/soc/qcom/storm.c
+++ b/sound/soc/qcom/storm.c
@@ -69,11 +69,6 @@ static struct snd_soc_dai_link storm_dai_link = {
.ops = &storm_soc_ops,
};
-static struct snd_soc_card storm_soc_card = {
- .name = "ipq806x-storm",
- .dev = NULL,
-};
-
static int storm_parse_of(struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link = card->dai_link;
@@ -99,14 +94,13 @@ static int storm_parse_of(struct snd_soc_card *card)
static int storm_platform_probe(struct platform_device *pdev)
{
- struct snd_soc_card *card = &storm_soc_card;
+ struct snd_soc_card *card;
int ret;
- if (card->dev) {
- dev_err(&pdev->dev, "%s() error, existing soundcard\n",
- __func__);
- return -ENODEV;
- }
+ card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
@@ -128,16 +122,12 @@ static int storm_platform_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret == -EPROBE_DEFER) {
- card->dev = NULL;
- return ret;
- } else if (ret) {
+ if (ret)
dev_err(&pdev->dev, "%s() error registering soundcard: %d\n",
__func__, ret);
- return ret;
- }
- return 0;
+ return ret;
+
}
#ifdef CONFIG_OF
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 0632a36852c8..3744c9ed5370 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -174,7 +174,8 @@ config SND_SOC_SMDK_WM8994_PCM
config SND_SOC_SPEYSIDE
tristate "Audio support for Wolfson Speyside"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C && SPI_MASTER
+ depends on SND_SOC_SAMSUNG && I2C && SPI_MASTER
+ depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM8996
select SND_SOC_WM9081
@@ -183,13 +184,15 @@ config SND_SOC_SPEYSIDE
config SND_SOC_TOBERMORY
tristate "Audio support for Wolfson Tobermory"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && INPUT && I2C
+ depends on SND_SOC_SAMSUNG && INPUT && I2C
+ depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM8962
config SND_SOC_BELLS
tristate "Audio support for Wolfson Bells"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA && I2C && SPI_MASTER
+ depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
+ depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM5102
select SND_SOC_WM5110
@@ -199,14 +202,16 @@ config SND_SOC_BELLS
config SND_SOC_LOWLAND
tristate "Audio support for Wolfson Lowland"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C
+ depends on SND_SOC_SAMSUNG && I2C
+ depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM5100
select SND_SOC_WM9081
config SND_SOC_LITTLEMILL
tristate "Audio support for Wolfson Littlemill"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C
+ depends on SND_SOC_SAMSUNG && I2C
+ depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select MFD_WM8994
select SND_SOC_WM8994
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index b92ab40d2be6..ea4ab374a223 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1493,7 +1493,7 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
.dai_type = TYPE_SEC,
};
-static struct platform_device_id samsung_i2s_driver_ids[] = {
+static const struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
.driver_data = (kernel_ulong_t)&i2sv3_dai_type,
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 5f156093101e..0d0f58208b75 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -72,7 +72,7 @@ static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
- snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT");
+ snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT");
/* At any time the WM9081 is active it will have this clock */
return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
index dfbe2db1c407..a0fe37fbed9f 100644
--- a/sound/soc/samsung/smartq_wm8987.c
+++ b/sound/soc/samsung/smartq_wm8987.c
@@ -137,8 +137,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int smartq_wm8987_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;
int err = 0;
/* set endpoints to not connected */
@@ -147,9 +146,6 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_nc_pin(dapm, "OUT3");
snd_soc_dapm_nc_pin(dapm, "ROUT1");
- /* set endpoints to default off mode */
- snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-
/* Headphone jack detection */
err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
SND_JACK_HEADPHONE, &smartq_jack,
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index d38595fbdab7..ff57b192d37d 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -86,8 +86,7 @@ static struct snd_soc_ops smdk_ops = {
static int smdk_wm8994_init_paiftx(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;
/* Other pins NC */
snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index 2dcb988bdff2..d1ae21c5e253 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -123,7 +123,7 @@ static void speyside_set_polarity(struct snd_soc_codec *codec,
gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
/* Re-run DAPM to make sure we're using the correct mic bias */
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
}
static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 9f48d75fa992..f1e5920654f6 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -137,15 +137,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod)
return mod->ops->name;
}
-struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod)
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
if (!mod || !mod->ops || !mod->ops->dma_req)
return NULL;
- return mod->ops->dma_req(mod);
+ return mod->ops->dma_req(io, mod);
}
-int rsnd_mod_init(struct rsnd_mod *mod,
+int rsnd_mod_init(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
@@ -160,6 +162,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
mod->ops = ops;
mod->type = type;
mod->clk = clk;
+ mod->priv = priv;
return ret;
}
@@ -170,13 +173,41 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
clk_unprepare(mod->clk);
}
+void rsnd_mod_interrupt(struct rsnd_mod *mod,
+ void (*callback)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io))
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_dai_stream *io;
+ struct rsnd_dai *rdai;
+ int i, j;
+
+ for_each_rsnd_dai(rdai, priv, j) {
+
+ for (i = 0; i < RSND_MOD_MAX; i++) {
+ io = &rdai->playback;
+ if (mod == io->mod[i])
+ callback(mod, io);
+
+ io = &rdai->capture;
+ if (mod == io->mod[i])
+ callback(mod, io);
+ }
+ }
+}
+
+int rsnd_io_is_working(struct rsnd_dai_stream *io)
+{
+ /* see rsnd_dai_stream_init/quit() */
+ return !!io->substream;
+}
+
/*
* settting function
*/
-u32 rsnd_get_adinr(struct rsnd_mod *mod)
+u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 adinr = runtime->channels;
@@ -199,26 +230,31 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
/*
* rsnd_dai functions
*/
-#define __rsnd_mod_call(mod, func, param...) \
+#define __rsnd_mod_call(mod, io, func, param...) \
({ \
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct device *dev = rsnd_priv_to_dev(priv); \
- u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \
- u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \
+ u32 mask = 0xF << __rsnd_mod_shift_##func; \
+ u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \
+ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
- if ((mod->status & mask) == call) { \
- dev_dbg(dev, "%s[%d] %s\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \
- ret = (mod)->ops->func(mod, param); \
- mod->status = (mod->status & ~mask) | (~call & mask); \
+ int called = 0; \
+ 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); \
} \
+ dev_dbg(dev, "%s[%d] 0x%08x %s\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \
+ called ? #func : ""); \
ret; \
})
-#define rsnd_mod_call(mod, func, param...) \
+#define rsnd_mod_call(mod, io, func, param...) \
(!(mod) ? -ENODEV : \
!((mod)->ops->func) ? 0 : \
- __rsnd_mod_call(mod, func, param))
+ __rsnd_mod_call(mod, io, func, param))
#define rsnd_dai_call(fn, io, param...) \
({ \
@@ -228,7 +264,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
mod = (io)->mod[i]; \
if (!mod) \
continue; \
- ret = rsnd_mod_call(mod, fn, param); \
+ ret = rsnd_mod_call(mod, io, fn, param); \
if (ret < 0) \
break; \
} \
@@ -252,7 +288,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
}
io->mod[mod->type] = mod;
- mod->io = io;
return 0;
}
@@ -260,7 +295,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
static void rsnd_dai_disconnect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
- mod->io = NULL;
io->mod[mod->type] = NULL;
}
@@ -272,9 +306,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
return priv->rdai + id;
}
+#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
{
- struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
return rsnd_rdai_get(priv, dai->id);
}
@@ -293,7 +328,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
return pos;
}
-void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
+bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
{
io->byte_pos += byte;
@@ -310,11 +345,27 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
io->next_period_byte = io->byte_per_period;
}
- snd_pcm_period_elapsed(substream);
+ return true;
}
+
+ return false;
}
-static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
+{
+ struct snd_pcm_substream *substream = io->substream;
+
+ /*
+ * this function should be called...
+ *
+ * - if rsnd_dai_pointer_update() returns true
+ * - without spin lock
+ */
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -326,8 +377,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
runtime->channels *
samples_to_bytes(runtime, 1);
io->next_period_byte = io->byte_per_period;
+}
- return 0;
+static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
+{
+ io->substream = NULL;
}
static
@@ -351,20 +405,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
int ret;
unsigned long flags;
- rsnd_lock(priv, flags);
+ spin_lock_irqsave(&priv->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- ret = rsnd_dai_stream_init(io, substream);
- if (ret < 0)
- goto dai_trigger_end;
+ rsnd_dai_stream_init(io, substream);
ret = rsnd_platform_call(priv, dai, start, ssi_id);
if (ret < 0)
@@ -390,13 +442,15 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = rsnd_platform_call(priv, dai, stop, ssi_id);
if (ret < 0)
goto dai_trigger_end;
+
+ rsnd_dai_stream_quit(io);
break;
default:
ret = -EINVAL;
}
dai_trigger_end:
- rsnd_unlock(priv, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
@@ -822,23 +876,27 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
}
if (change)
- cfg->update(mod);
+ cfg->update(cfg->io, mod);
return change;
}
static int __rsnd_kctrl_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
struct rsnd_kctrl_cfg *cfg,
- void (*update)(struct rsnd_mod *mod))
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod))
{
+ struct snd_soc_card *soc_card = rtd->card;
struct snd_card *card = rtd->card->snd_card;
struct snd_kcontrol *kctrl;
struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = name,
.info = rsnd_kctrl_info,
+ .index = rtd - soc_card->rtd,
.get = rsnd_kctrl_get,
.put = rsnd_kctrl_put,
.private_value = (unsigned long)cfg,
@@ -858,6 +916,7 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
cfg->update = update;
cfg->card = card;
cfg->kctrl = kctrl;
+ cfg->io = io;
return 0;
}
@@ -868,36 +927,42 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
}
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
- void (*update)(struct rsnd_mod *mod),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg,
u32 max)
{
_cfg->cfg.max = max;
_cfg->cfg.size = RSND_DVC_CHANNELS;
_cfg->cfg.val = _cfg->val;
- return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update);
+ return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
}
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
- void (*update)(struct rsnd_mod *mod),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_s *_cfg,
u32 max)
{
_cfg->cfg.max = max;
_cfg->cfg.size = 1;
_cfg->cfg.val = &_cfg->val;
- return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update);
+ return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
}
int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
struct rsnd_kctrl_cfg_s *_cfg,
- void (*update)(struct rsnd_mod *mod),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
const char * const *texts,
u32 max)
{
@@ -905,7 +970,7 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod,
_cfg->cfg.size = 1;
_cfg->cfg.val = &_cfg->val;
_cfg->cfg.texts = texts;
- return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update);
+ return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
}
/*
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 144308f15fb3..d306e298c63d 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -32,11 +32,12 @@ struct rsnd_dma_ctrl {
/*
* Audio DMAC
*/
-static void rsnd_dmaen_complete(void *data)
+static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
- struct rsnd_dma *dma = (struct rsnd_dma *)data;
- struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ bool elapsed = false;
+ unsigned long flags;
/*
* Renesas sound Gen1 needs 1 DMAC,
@@ -49,23 +50,36 @@ static void rsnd_dmaen_complete(void *data)
* rsnd_dai_pointer_update() will be called twice,
* ant it will breaks io->byte_pos
*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (rsnd_io_is_working(io))
+ elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
- rsnd_dai_pointer_update(io, io->byte_per_period);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (elapsed)
+ rsnd_dai_period_elapsed(io);
}
-static void rsnd_dmaen_stop(struct rsnd_dma *dma)
+static void rsnd_dmaen_complete(void *data)
+{
+ struct rsnd_mod *mod = data;
+
+ rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
+}
+
+static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
dmaengine_terminate_all(dmaen->chan);
}
-static void rsnd_dmaen_start(struct rsnd_dma *dma)
+static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc;
@@ -84,7 +98,7 @@ static void rsnd_dmaen_start(struct rsnd_dma *dma)
}
desc->callback = rsnd_dmaen_complete;
- desc->callback_param = dma;
+ desc->callback_param = mod;
if (dmaengine_submit(desc) < 0) {
dev_err(dev, "dmaengine_submit() fail\n");
@@ -115,7 +129,8 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
return chan;
}
-static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from,
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod_from,
struct rsnd_mod *mod_to)
{
if ((!mod_from && !mod_to) ||
@@ -123,19 +138,19 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from,
return NULL;
if (mod_from)
- return rsnd_mod_dma_req(mod_from);
+ return rsnd_mod_dma_req(io, mod_from);
else
- return rsnd_mod_dma_req(mod_to);
+ return rsnd_mod_dma_req(io, mod_to);
}
-static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
+ struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_slave_config cfg = {};
- struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_play = rsnd_io_is_play(io);
int ret;
@@ -145,7 +160,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
}
if (dev->of_node) {
- dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to);
+ dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
} else {
dma_cap_mask_t mask;
@@ -177,7 +192,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
return 0;
rsnd_dma_init_err:
- rsnd_dma_quit(dma);
+ rsnd_dma_quit(io, dma);
rsnd_dma_channel_err:
/*
@@ -189,7 +204,7 @@ rsnd_dma_channel_err:
return -EAGAIN;
}
-static void rsnd_dmaen_quit(struct rsnd_dma *dma)
+static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
@@ -238,9 +253,9 @@ static const u8 gen2_id_table_cmd[] = {
0x38, /* SCU_CMD1 */
};
-static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
+static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
@@ -268,11 +283,12 @@ static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
return entry[id];
}
-static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from,
+static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod_from,
struct rsnd_mod *mod_to)
{
- return (rsnd_dmapp_get_id(mod_from) << 24) +
- (rsnd_dmapp_get_id(mod_to) << 16);
+ return (rsnd_dmapp_get_id(io, mod_from) << 24) +
+ (rsnd_dmapp_get_id(io, mod_to) << 16);
}
#define rsnd_dmapp_addr(dmac, dma, reg) \
@@ -299,7 +315,7 @@ static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
}
-static void rsnd_dmapp_stop(struct rsnd_dma *dma)
+static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
int i;
@@ -312,7 +328,7 @@ static void rsnd_dmapp_stop(struct rsnd_dma *dma)
}
}
-static void rsnd_dmapp_start(struct rsnd_dma *dma)
+static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
@@ -321,19 +337,21 @@ static void rsnd_dmapp_start(struct rsnd_dma *dma)
rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR);
}
-static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
+ struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+ 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);
dmapp->dmapp_id = dmac->dmapp_num;
- dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE;
+ dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE;
dmac->dmapp_num++;
- rsnd_dmapp_stop(dma);
+ rsnd_dmapp_stop(io, dma);
dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
@@ -386,12 +404,12 @@ static struct rsnd_dma_ops rsnd_dmapp_ops = {
#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i))
static dma_addr_t
-rsnd_gen2_dma_addr(struct rsnd_priv *priv,
+rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
int is_play, int is_from)
{
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
@@ -438,7 +456,7 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv,
dev_err(dev, "DVC is selected without SRC\n");
/* use SSIU or SSI ? */
- if (is_ssi && rsnd_ssi_use_busif(mod))
+ if (is_ssi && rsnd_ssi_use_busif(io, mod))
is_ssi++;
return (is_from) ?
@@ -446,10 +464,12 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv,
dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
}
-static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv,
+static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
int is_play, int is_from)
{
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+
/*
* gen1 uses default DMA addr
*/
@@ -459,17 +479,17 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv,
if (!mod)
return 0;
- return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
+ return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
}
#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
static void rsnd_dma_of_path(struct rsnd_dma *dma,
+ struct rsnd_dai_stream *io,
int is_play,
struct rsnd_mod **mod_from,
struct rsnd_mod **mod_to)
{
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
@@ -524,17 +544,17 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
}
}
-void rsnd_dma_stop(struct rsnd_dma *dma)
+void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
- dma->ops->stop(dma);
+ dma->ops->stop(io, dma);
}
-void rsnd_dma_start(struct rsnd_dma *dma)
+void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
- dma->ops->start(dma);
+ dma->ops->start(io, dma);
}
-void rsnd_dma_quit(struct rsnd_dma *dma)
+void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@@ -543,15 +563,14 @@ void rsnd_dma_quit(struct rsnd_dma *dma)
if (!dmac)
return;
- dma->ops->quit(dma);
+ dma->ops->quit(io, dma);
}
-int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
+int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
{
- struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_mod *mod_from;
struct rsnd_mod *mod_to;
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
int is_play = rsnd_io_is_play(io);
@@ -564,10 +583,10 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
if (!dmac)
return -EAGAIN;
- rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
+ rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to);
- dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1);
- dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0);
+ dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+ dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
/* for Gen2 */
if (mod_from && mod_to)
@@ -579,7 +598,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
if (rsnd_is_gen1(priv))
dma->ops = &rsnd_dmaen_ops;
- return dma->ops->init(priv, dma, id, mod_from, mod_to);
+ return dma->ops->init(io, dma, id, mod_from, mod_to);
}
int rsnd_dma_probe(struct platform_device *pdev,
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index e5fcb062ad77..36fc020cbc18 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -63,7 +63,8 @@ static const char * const dvc_ramp_rate[] = {
"0.125 dB/8192 steps", /* 10111 */
};
-static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
+static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 val[RSND_DVC_CHANNELS];
@@ -120,6 +121,7 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
}
static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
@@ -134,9 +136,9 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
}
static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod);
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);
@@ -168,10 +170,10 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
rsnd_mod_write(dvc_mod, DVC_DVUIR, 1);
- rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
+ rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io));
/* ch0/ch1 Volume */
- rsnd_dvc_volume_update(dvc_mod);
+ rsnd_dvc_volume_update(io, dvc_mod);
rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
@@ -181,6 +183,7 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
}
static int rsnd_dvc_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_hw_stop(mod);
@@ -189,6 +192,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
}
static int rsnd_dvc_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0x10);
@@ -197,6 +201,7 @@ static int rsnd_dvc_start(struct rsnd_mod *mod,
}
static int rsnd_dvc_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0);
@@ -205,15 +210,15 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
}
static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
int is_play = rsnd_io_is_play(io);
int ret;
/* Volume */
- ret = rsnd_kctrl_new_m(mod, rtd,
+ ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
"DVC Out Playback Volume" : "DVC In Capture Volume",
rsnd_dvc_volume_update,
@@ -222,7 +227,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return ret;
/* Mute */
- ret = rsnd_kctrl_new_m(mod, rtd,
+ ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
"DVC Out Mute Switch" : "DVC In Mute Switch",
rsnd_dvc_volume_update,
@@ -231,7 +236,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return ret;
/* Ramp */
- ret = rsnd_kctrl_new_s(mod, rtd,
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
is_play ?
"DVC Out Ramp Switch" : "DVC In Ramp Switch",
rsnd_dvc_volume_update,
@@ -239,7 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- ret = rsnd_kctrl_new_e(mod, rtd,
+ ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
&dvc->rup,
@@ -248,7 +253,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- ret = rsnd_kctrl_new_e(mod, rtd,
+ ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
&dvc->rdown,
@@ -261,7 +266,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return 0;
}
-static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod)
+static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@@ -366,7 +372,7 @@ int rsnd_dvc_probe(struct platform_device *pdev,
dvc->info = &info->dvc_info[i];
- ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops,
+ ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
if (ret)
return ret;
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 4e6de6804cfb..09fcc54a8ee0 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -165,18 +165,18 @@ void rsnd_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);
+u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
/*
* R-Car DMA
*/
struct rsnd_dma;
struct rsnd_dma_ops {
- void (*start)(struct rsnd_dma *dma);
- void (*stop)(struct rsnd_dma *dma);
- int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+ 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_dma *dma);
+ void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
};
struct rsnd_dmaen {
@@ -200,10 +200,10 @@ struct rsnd_dma {
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
-void rsnd_dma_start(struct rsnd_dma *dma);
-void rsnd_dma_stop(struct rsnd_dma *dma);
-int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id);
-void rsnd_dma_quit(struct rsnd_dma *dma);
+void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
+void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
+int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id);
+void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int rsnd_dma_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
@@ -224,25 +224,35 @@ enum rsnd_mod_type {
struct rsnd_mod_ops {
char *name;
- struct dma_chan* (*dma_req)(struct rsnd_mod *mod);
+ struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod);
int (*probe)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*remove)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*init)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*quit)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*start)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*stop)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*pcm_new)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd);
int (*hw_params)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params);
int (*fallback)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
};
@@ -252,32 +262,43 @@ struct rsnd_mod {
enum rsnd_mod_type type;
struct rsnd_mod_ops *ops;
struct rsnd_dma dma;
- struct rsnd_dai_stream *io;
+ struct rsnd_priv *priv;
struct clk *clk;
u32 status;
};
/*
* status
*
- * bit
- * 0 0: probe 1: remove
- * 1 0: init 1: quit
- * 2 0: start 1: stop
- * 3 0: pcm_new
- * 4 0: fallback
+ * 0xH0000CBA
+ *
+ * A 0: probe 1: remove
+ * B 0: init 1: quit
+ * C 0: start 1: stop
*
- * 31 bit is always called (see __rsnd_mod_call)
- * 31 0: hw_params
+ * H is always called (see __rsnd_mod_call)
+ * H 0: pcm_new
+ * H 0: fallback
+ * H 0: hw_params
*/
#define __rsnd_mod_shift_probe 0
#define __rsnd_mod_shift_remove 0
-#define __rsnd_mod_shift_init 1
-#define __rsnd_mod_shift_quit 1
-#define __rsnd_mod_shift_start 2
-#define __rsnd_mod_shift_stop 2
-#define __rsnd_mod_shift_pcm_new 3
-#define __rsnd_mod_shift_fallback 4
-#define __rsnd_mod_shift_hw_params 31 /* always called */
+#define __rsnd_mod_shift_init 4
+#define __rsnd_mod_shift_quit 4
+#define __rsnd_mod_shift_start 8
+#define __rsnd_mod_shift_stop 8
+#define __rsnd_mod_shift_pcm_new 28 /* always called */
+#define __rsnd_mod_shift_fallback 28 /* always called */
+#define __rsnd_mod_shift_hw_params 28 /* always called */
+
+#define __rsnd_mod_add_probe 1
+#define __rsnd_mod_add_remove -1
+#define __rsnd_mod_add_init 1
+#define __rsnd_mod_add_quit -1
+#define __rsnd_mod_add_start 1
+#define __rsnd_mod_add_stop -1
+#define __rsnd_mod_add_pcm_new 0
+#define __rsnd_mod_add_fallback 0
+#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
#define __rsnd_mod_call_remove 1
@@ -289,21 +310,25 @@ struct rsnd_mod {
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
-#define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod)))
+#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
-#define rsnd_mod_to_io(mod) ((mod)->io)
#define rsnd_mod_id(mod) ((mod)->id)
#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk)
-int rsnd_mod_init(struct rsnd_mod *mod,
+int rsnd_mod_init(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
int id);
void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
-struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod);
+void rsnd_mod_interrupt(struct rsnd_mod *mod,
+ void (*callback)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io));
/*
* R-Car sound DAI
@@ -328,7 +353,7 @@ struct rsnd_dai_stream {
#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
#define rsnd_io_to_runtime(io) ((io)->substream ? \
(io)->substream->runtime : NULL)
-
+int rsnd_io_is_working(struct rsnd_dai_stream *io);
struct rsnd_dai {
char name[RSND_DAI_NAME_SIZE];
@@ -354,7 +379,8 @@ struct rsnd_dai {
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
-void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
/*
@@ -449,8 +475,6 @@ struct rsnd_priv {
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
#define rsnd_priv_to_info(priv) ((priv)->info)
-#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
-#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
/*
* rsnd_kctrl
@@ -460,7 +484,8 @@ struct rsnd_kctrl_cfg {
unsigned int size;
u32 *val;
const char * const *texts;
- void (*update)(struct rsnd_mod *mod);
+ void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+ struct rsnd_dai_stream *io;
struct snd_card *card;
struct snd_kcontrol *kctrl;
};
@@ -480,22 +505,28 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg);
#define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg))
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
- void (*update)(struct rsnd_mod *mod),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg,
u32 max);
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
- void (*update)(struct rsnd_mod *mod),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_s *_cfg,
u32 max);
int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
struct rsnd_kctrl_cfg_s *_cfg,
- void (*update)(struct rsnd_mod *mod),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
const char * const *texts,
u32 max);
@@ -512,8 +543,10 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime);
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai_stream *io,
int use_busif);
-int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod);
+int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai_stream *io);
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod);
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
@@ -530,7 +563,7 @@ void rsnd_ssi_remove(struct platform_device *pdev,
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_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
/*
* R-Car DVC
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
index a68517afe615..84e935711e29 100644
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ b/sound/soc/sh/rcar/rsrc-card.c
@@ -45,61 +45,48 @@ static const struct of_device_id rsrc_card_of_match[] = {
};
MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
+#define DAI_NAME_NUM 32
struct rsrc_card_dai {
- const char *name;
unsigned int fmt;
unsigned int sysclk;
struct clk *clk;
+ char dai_name[DAI_NAME_NUM];
};
-#define RSRC_FB_NUM 2 /* FE/BE */
#define IDX_CPU 0
#define IDX_CODEC 1
struct rsrc_card_priv {
struct snd_soc_card snd_card;
- struct rsrc_card_dai_props {
- struct rsrc_card_dai cpu_dai;
- struct rsrc_card_dai codec_dai;
- } dai_props[RSRC_FB_NUM];
struct snd_soc_codec_conf codec_conf;
- struct snd_soc_dai_link dai_link[RSRC_FB_NUM];
+ struct rsrc_card_dai *dai_props;
+ struct snd_soc_dai_link *dai_link;
+ int dai_num;
u32 convert_rate;
};
#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
-#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
-#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i)
+#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
+#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i))
#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
static int rsrc_card_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct rsrc_card_dai_props *dai_props =
- &priv->dai_props[rtd - rtd->card->rtd];
- int ret;
-
- ret = clk_prepare_enable(dai_props->cpu_dai.clk);
- if (ret)
- return ret;
+ struct rsrc_card_dai *dai_props =
+ rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
- ret = clk_prepare_enable(dai_props->codec_dai.clk);
- if (ret)
- clk_disable_unprepare(dai_props->cpu_dai.clk);
-
- return ret;
+ return clk_prepare_enable(dai_props->clk);
}
static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct rsrc_card_dai_props *dai_props =
- &priv->dai_props[rtd - rtd->card->rtd];
-
- clk_disable_unprepare(dai_props->cpu_dai.clk);
+ struct rsrc_card_dai *dai_props =
+ rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
- clk_disable_unprepare(dai_props->codec_dai.clk);
+ clk_disable_unprepare(dai_props->clk);
}
static struct snd_soc_ops rsrc_card_ops = {
@@ -107,21 +94,31 @@ static struct snd_soc_ops rsrc_card_ops = {
.shutdown = rsrc_card_shutdown,
};
-static int __rsrc_card_dai_init(struct snd_soc_dai *dai,
- struct rsrc_card_dai *set)
+static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
+ struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai;
+ struct snd_soc_dai_link *dai_link;
+ struct rsrc_card_dai *dai_props;
+ int num = rtd - rtd->card->rtd;
int ret;
- if (set->fmt) {
- ret = snd_soc_dai_set_fmt(dai, set->fmt);
+ dai_link = rsrc_priv_to_link(priv, num);
+ dai_props = rsrc_priv_to_props(priv, num);
+ dai = dai_link->dynamic ?
+ rtd->cpu_dai :
+ rtd->codec_dai;
+
+ if (dai_props->fmt) {
+ ret = snd_soc_dai_set_fmt(dai, dai_props->fmt);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_fmt error\n");
goto err;
}
}
- if (set->sysclk) {
- ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+ if (dai_props->sysclk) {
+ ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_sysclk error\n");
goto err;
@@ -134,27 +131,6 @@ err:
return ret;
}
-static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_dai *codec = rtd->codec_dai;
- struct snd_soc_dai *cpu = rtd->cpu_dai;
- struct rsrc_card_dai_props *dai_props;
- int num, ret;
-
- num = rtd - rtd->card->rtd;
- dai_props = &priv->dai_props[num];
- ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai);
- if (ret < 0)
- return ret;
-
- ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -170,40 +146,47 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static int
-rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
- struct device_node *np,
- struct rsrc_card_dai *dai,
- struct snd_soc_dai_link *dai_link,
- int *args_count)
+static int rsrc_card_parse_daifmt(struct device_node *node,
+ struct device_node *np,
+ struct rsrc_card_priv *priv,
+ int idx, bool is_fe)
{
- struct device *dev = rsrc_priv_to_dev(priv);
- const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
- struct of_phandle_args args;
- struct device_node **p_node;
- struct clk *clk;
- const char **dai_name;
- const char **name;
- u32 val;
- int ret;
+ struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+ struct device_node *bitclkmaster = NULL;
+ struct device_node *framemaster = NULL;
+ struct device_node *codec = is_fe ? NULL : np;
+ unsigned int daifmt;
- if (args_count) {
- p_node = &dai_link->cpu_of_node;
- dai_name = &dai_link->cpu_dai_name;
- name = &dai_link->cpu_name;
- } else {
- p_node = &dai_link->codec_of_node;
- dai_name = &dai_link->codec_dai_name;
- name = &dai_link->codec_name;
- }
+ daifmt = snd_soc_of_parse_daifmt(node, NULL,
+ &bitclkmaster, &framemaster);
+ daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
- if (!np) {
- /* use snd-soc-dummy */
- *p_node = NULL;
- *dai_name = "snd-soc-dummy-dai";
- *name = "snd-soc-dummy";
- return 0;
- }
+ if (!bitclkmaster && !framemaster)
+ return -EINVAL;
+
+ if (codec == bitclkmaster)
+ daifmt |= (codec == framemaster) ?
+ SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+ else
+ daifmt |= (codec == framemaster) ?
+ SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+
+ dai_props->fmt = daifmt;
+
+ of_node_put(bitclkmaster);
+ of_node_put(framemaster);
+
+ return 0;
+}
+
+static int rsrc_card_parse_links(struct device_node *np,
+ struct rsrc_card_priv *priv,
+ int idx, bool is_fe)
+{
+ struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
+ struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+ struct of_phandle_args args;
+ int ret;
/*
* Get node via "sound-dai = <&phandle port>"
@@ -214,30 +197,82 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
if (ret)
return ret;
- *p_node = args.np;
+ if (is_fe) {
+ /* BE is dummy */
+ dai_link->codec_of_node = NULL;
+ dai_link->codec_dai_name = "snd-soc-dummy-dai";
+ dai_link->codec_name = "snd-soc-dummy";
+
+ /* FE settings */
+ dai_link->dynamic = 1;
+ dai_link->dpcm_merged_format = 1;
+ dai_link->cpu_of_node = args.np;
+ snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
+
+ /* set dai_name */
+ snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
+ dai_link->cpu_dai_name);
+
+ /*
+ * In soc_bind_dai_link() will check cpu name after
+ * of_node matching if dai_link has cpu_dai_name.
+ * but, it will never match if name was created by
+ * fmt_single_name() remove cpu_dai_name if cpu_args
+ * was 0. See:
+ * fmt_single_name()
+ * fmt_multiple_name()
+ */
+ if (!args.args_count)
+ dai_link->cpu_dai_name = NULL;
+ } else {
+ struct device *dev = rsrc_priv_to_dev(priv);
+ const struct rsrc_card_of_data *of_data;
- /* Get dai->name */
- ret = snd_soc_of_get_dai_name(np, dai_name);
- if (ret < 0)
- return ret;
+ of_data = rsrc_dev_to_of_data(dev);
- /*
- * FIXME
- *
- * rsrc assumes DPCM playback/capture
- */
- dai_link->dpcm_playback = 1;
- dai_link->dpcm_capture = 1;
+ /* FE is dummy */
+ dai_link->cpu_of_node = NULL;
+ dai_link->cpu_dai_name = "snd-soc-dummy-dai";
+ dai_link->cpu_name = "snd-soc-dummy";
- if (args_count) {
- *args_count = args.args_count;
- dai_link->dynamic = 1;
- } else {
- dai_link->no_pcm = 1;
- priv->codec_conf.of_node = (*p_node);
- priv->codec_conf.name_prefix = of_data->prefix;
+ /* BE settings */
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
+ dai_link->codec_of_node = args.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;
+
+ /* set dai_name */
+ snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s",
+ dai_link->codec_dai_name);
}
+ /* Simple Card assumes platform == cpu */
+ dai_link->platform_of_node = dai_link->cpu_of_node;
+ dai_link->dpcm_playback = 1;
+ dai_link->dpcm_capture = 1;
+ dai_link->name = dai_props->dai_name;
+ dai_link->stream_name = dai_props->dai_name;
+ dai_link->ops = &rsrc_card_ops;
+ dai_link->init = rsrc_card_dai_init;
+
+ return 0;
+}
+
+static int rsrc_card_parse_clk(struct device_node *np,
+ struct rsrc_card_priv *priv,
+ int idx, bool is_fe)
+{
+ struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
+ struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+ struct clk *clk;
+ struct device_node *of_np = is_fe ? dai_link->cpu_of_node :
+ dai_link->codec_of_node;
+ u32 val;
+
/*
* Parse dai->sysclk come from "clocks = <&xxx>"
* (if system has common clock)
@@ -246,173 +281,92 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
*/
if (of_property_read_bool(np, "clocks")) {
clk = of_clk_get(np, 0);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- return ret;
- }
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- dai->sysclk = clk_get_rate(clk);
- dai->clk = clk;
+ dai_props->sysclk = clk_get_rate(clk);
+ dai_props->clk = clk;
} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
- dai->sysclk = val;
+ dai_props->sysclk = val;
} else {
- clk = of_clk_get(args.np, 0);
+ clk = of_clk_get(of_np, 0);
if (!IS_ERR(clk))
- dai->sysclk = clk_get_rate(clk);
+ dai_props->sysclk = clk_get_rate(clk);
}
return 0;
}
-static int rsrc_card_parse_daifmt(struct device_node *node,
- struct rsrc_card_priv *priv,
- struct device_node *codec,
- int idx)
-{
- struct device_node *bitclkmaster = NULL;
- struct device_node *framemaster = NULL;
- struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
- struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai;
- struct rsrc_card_dai *codec_dai = &dai_props->codec_dai;
- unsigned int daifmt;
-
- daifmt = snd_soc_of_parse_daifmt(node, NULL,
- &bitclkmaster, &framemaster);
- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
- if (!bitclkmaster && !framemaster)
- return -EINVAL;
-
- if (codec == bitclkmaster)
- daifmt |= (codec == framemaster) ?
- SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
- else
- daifmt |= (codec == framemaster) ?
- SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
-
- cpu_dai->fmt = daifmt;
- codec_dai->fmt = daifmt;
-
- of_node_put(bitclkmaster);
- of_node_put(framemaster);
-
- return 0;
-}
-
static int rsrc_card_dai_link_of(struct device_node *node,
+ struct device_node *np,
struct rsrc_card_priv *priv,
int idx)
{
struct device *dev = rsrc_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
- struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
- struct device_node *cpu = NULL;
- struct device_node *codec = NULL;
- char *name;
- char prop[128];
- int ret, cpu_args;
-
- cpu = of_get_child_by_name(node, "cpu");
- codec = of_get_child_by_name(node, "codec");
-
- if (!cpu || !codec) {
- ret = -EINVAL;
- dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
- goto dai_link_of_err;
- }
+ struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+ bool is_fe = false;
+ int ret;
- ret = rsrc_card_parse_daifmt(node, priv, codec, idx);
- if (ret < 0)
- goto dai_link_of_err;
+ if (0 == strcmp(np->name, "cpu"))
+ is_fe = true;
- ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL,
- &dai_props->cpu_dai,
- dai_link,
- &cpu_args);
+ ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe);
if (ret < 0)
- goto dai_link_of_err;
+ return ret;
- ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL,
- &dai_props->codec_dai,
- dai_link,
- NULL);
+ ret = rsrc_card_parse_links(np, priv, idx, is_fe);
if (ret < 0)
- goto dai_link_of_err;
-
- if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
- ret = -EINVAL;
- goto dai_link_of_err;
- }
-
- /* Simple Card assumes platform == cpu */
- dai_link->platform_of_node = dai_link->cpu_of_node;
-
- /* DAI link name is created from CPU/CODEC dai name */
- name = devm_kzalloc(dev,
- strlen(dai_link->cpu_dai_name) +
- strlen(dai_link->codec_dai_name) + 2,
- GFP_KERNEL);
- if (!name) {
- ret = -ENOMEM;
- goto dai_link_of_err;
- }
-
- sprintf(name, "%s-%s", dai_link->cpu_dai_name,
- dai_link->codec_dai_name);
- dai_link->name = dai_link->stream_name = name;
- dai_link->ops = &rsrc_card_ops;
- dai_link->init = rsrc_card_dai_init;
-
- if (idx == IDX_CODEC)
- dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
-
- dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
- dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
- dai_link->cpu_dai_name,
- dai_props->cpu_dai.fmt,
- dai_props->cpu_dai.sysclk);
- dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
- dai_link->codec_dai_name,
- dai_props->codec_dai.fmt,
- dai_props->codec_dai.sysclk);
+ return ret;
- /*
- * In soc_bind_dai_link() will check cpu name after
- * of_node matching if dai_link has cpu_dai_name.
- * but, it will never match if name was created by
- * fmt_single_name() remove cpu_dai_name if cpu_args
- * was 0. See:
- * fmt_single_name()
- * fmt_multiple_name()
- */
- if (!cpu_args)
- dai_link->cpu_dai_name = NULL;
+ ret = rsrc_card_parse_clk(np, priv, idx, is_fe);
+ if (ret < 0)
+ return ret;
-dai_link_of_err:
- of_node_put(cpu);
- of_node_put(codec);
+ dev_dbg(dev, "\t%s / %04x / %d\n",
+ dai_props->dai_name,
+ dai_props->fmt,
+ dai_props->sysclk);
return ret;
}
static int rsrc_card_parse_of(struct device_node *node,
- struct rsrc_card_priv *priv)
+ struct rsrc_card_priv *priv,
+ struct device *dev)
{
- struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+ struct rsrc_card_dai *props;
+ struct snd_soc_dai_link *links;
+ struct device_node *np;
int ret;
- int i;
+ int i, num;
if (!node)
return -EINVAL;
- /* Parse the card name from DT */
- snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
+ num = of_get_child_count(node);
+ props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL);
+ links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL);
+ if (!props || !links)
+ return -ENOMEM;
+
+ priv->dai_props = props;
+ priv->dai_link = links;
+ priv->dai_num = num;
- /* DAPM routes */
+ /* Init snd_soc_card */
+ priv->snd_card.owner = THIS_MODULE;
+ priv->snd_card.dev = dev;
+ priv->snd_card.dai_link = priv->dai_link;
+ 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;
+ /* Parse the card name from DT */
+ snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
+
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
@@ -420,11 +374,12 @@ static int rsrc_card_parse_of(struct device_node *node,
priv->snd_card.name ? priv->snd_card.name : "",
priv->convert_rate);
- /* FE/BE */
- for (i = 0; i < RSRC_FB_NUM; i++) {
- ret = rsrc_card_dai_link_of(node, priv, i);
+ i = 0;
+ for_each_child_of_node(node, np) {
+ ret = rsrc_card_dai_link_of(node, np, priv, i);
if (ret < 0)
return ret;
+ i++;
}
if (!priv->snd_card.name)
@@ -451,7 +406,6 @@ static int rsrc_card_unref(struct snd_soc_card *card)
static int rsrc_card_probe(struct platform_device *pdev)
{
struct rsrc_card_priv *priv;
- struct snd_soc_dai_link *dai_link;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
@@ -461,16 +415,7 @@ static int rsrc_card_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- /* Init snd_soc_card */
- priv->snd_card.owner = THIS_MODULE;
- priv->snd_card.dev = dev;
- dai_link = priv->dai_link;
- priv->snd_card.dai_link = dai_link;
- priv->snd_card.num_links = RSRC_FB_NUM;
- priv->snd_card.codec_conf = &priv->codec_conf;
- priv->snd_card.num_configs = 1;
-
- ret = rsrc_card_parse_of(np, priv);
+ ret = rsrc_card_parse_of(np, priv, dev);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 3beb32eb412a..c61c17180142 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -117,10 +117,10 @@ struct rsnd_src {
/*
* Gen1/Gen2 common functions
*/
-static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
+static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_play = rsnd_io_is_play(io);
return rsnd_dma_request_channel(rsnd_src_of_node(priv),
@@ -129,9 +129,9 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
}
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai_stream *io,
int use_busif)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod);
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);
@@ -174,7 +174,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
u32 mask = ~0;
rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
- rsnd_get_adinr(ssi_mod));
+ rsnd_get_adinr(ssi_mod, io));
rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
@@ -196,7 +196,8 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
return 0;
}
-int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod)
+int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai_stream *io)
{
/*
* DMA settings for SSIU
@@ -235,10 +236,9 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
return 0;
}
-static u32 rsnd_src_convert_rate(struct rsnd_src *src)
+static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
+ struct rsnd_src *src)
{
- struct rsnd_mod *mod = &src->mod;
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 convert_rate;
@@ -274,7 +274,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
* return convert rate if SRC is used,
* otherwise, return runtime->rate as usual
*/
- rate = rsnd_src_convert_rate(src);
+ rate = rsnd_src_convert_rate(io, src);
}
if (!rate)
@@ -283,12 +283,12 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
return rate;
}
-static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
+static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(src);
+ u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fsrate = 0;
if (convert_rate)
@@ -299,7 +299,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
rsnd_mod_write(mod, SRC_SWRSR, 1);
/* Set channel number and output bit length */
- rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
+ rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod, io));
/* Enable the initial value of IFS */
if (fsrate) {
@@ -316,6 +316,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
}
static int rsnd_src_hw_params(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *fe_params)
{
@@ -372,6 +373,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
}
static int rsnd_src_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
@@ -411,9 +413,9 @@ static int rsnd_src_stop(struct rsnd_mod *mod)
/*
* Gen1 functions
*/
-static int rsnd_src_set_route_gen1(struct rsnd_mod *mod)
+static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct src_route_config {
u32 mask;
int shift;
@@ -448,13 +450,13 @@ static int rsnd_src_set_route_gen1(struct rsnd_mod *mod)
return 0;
}
-static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod)
+static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- u32 convert_rate = rsnd_src_convert_rate(src);
+ u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 mask;
u32 val;
int shift;
@@ -506,12 +508,13 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod)
return 0;
}
-static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
+static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
- ret = rsnd_src_set_convert_rate(mod);
+ ret = rsnd_src_set_convert_rate(mod, io);
if (ret < 0)
return ret;
@@ -523,7 +526,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
/* Gen1/Gen2 are not compatible */
- if (rsnd_src_convert_rate(src))
+ if (rsnd_src_convert_rate(io, src))
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
@@ -532,6 +535,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
}
static int rsnd_src_init_gen1(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
@@ -540,15 +544,15 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- ret = rsnd_src_set_route_gen1(mod);
+ ret = rsnd_src_set_route_gen1(io, mod);
if (ret < 0)
return ret;
- ret = rsnd_src_set_convert_rate_gen1(mod);
+ ret = rsnd_src_set_convert_rate_gen1(mod, io);
if (ret < 0)
return ret;
- ret = rsnd_src_set_convert_timing_gen1(mod);
+ ret = rsnd_src_set_convert_timing_gen1(io, mod);
if (ret < 0)
return ret;
@@ -556,6 +560,7 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod,
}
static int rsnd_src_start_gen1(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int id = rsnd_mod_id(mod);
@@ -566,6 +571,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
}
static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int id = rsnd_mod_id(mod);
@@ -643,9 +649,9 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod)
return ret;
}
-static int _rsnd_src_start_gen2(struct rsnd_mod *mod)
+static int _rsnd_src_start_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11;
rsnd_mod_write(mod, SRC_CTRL, val);
@@ -670,13 +676,16 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
return rsnd_src_stop(mod);
}
-static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
+static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
- struct rsnd_mod *mod = data;
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- if (!io)
- return IRQ_NONE;
+ spin_lock(&priv->lock);
+
+ /* ignore all cases if not working */
+ if (!rsnd_io_is_working(io))
+ goto rsnd_src_interrupt_gen2_out;
if (rsnd_src_error_record_gen2(mod)) {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@@ -688,22 +697,32 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
_rsnd_src_stop_gen2(mod);
if (src->err < 1024)
- _rsnd_src_start_gen2(mod);
+ _rsnd_src_start_gen2(mod, io);
else
dev_warn(dev, "no more SRC restart\n");
}
+rsnd_src_interrupt_gen2_out:
+ spin_unlock(&priv->lock);
+}
+
+static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
+{
+ struct rsnd_mod *mod = data;
+
+ rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2);
+
return IRQ_HANDLED;
}
-static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
+static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(src);
+ u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 cr, route;
uint ratio;
int ret;
@@ -721,7 +740,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
return -EINVAL;
}
- ret = rsnd_src_set_convert_rate(mod);
+ ret = rsnd_src_set_convert_rate(mod, io);
if (ret < 0)
return ret;
@@ -757,12 +776,12 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
return 0;
}
-static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod)
+static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(src);
+ u32 convert_rate = rsnd_src_convert_rate(io, src);
int ret;
if (convert_rate)
@@ -776,6 +795,7 @@ static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod)
}
static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
@@ -797,7 +817,7 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
return ret;
}
- ret = rsnd_dma_init(priv,
+ ret = rsnd_dma_init(io,
rsnd_mod_to_dma(mod),
src->info->dma_id);
@@ -805,14 +825,16 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
}
static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_dma_quit(rsnd_mod_to_dma(mod));
+ rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
return 0;
}
static int rsnd_src_init_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
@@ -821,11 +843,11 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- ret = rsnd_src_set_convert_rate_gen2(mod);
+ ret = rsnd_src_set_convert_rate_gen2(mod, io);
if (ret < 0)
return ret;
- ret = rsnd_src_set_convert_timing_gen2(mod);
+ ret = rsnd_src_set_convert_timing_gen2(io, mod);
if (ret < 0)
return ret;
@@ -833,31 +855,33 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
}
static int rsnd_src_start_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_dma_start(rsnd_mod_to_dma(mod));
+ rsnd_dma_start(io, rsnd_mod_to_dma(mod));
- return _rsnd_src_start_gen2(mod);
+ return _rsnd_src_start_gen2(mod, io);
}
static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
ret = _rsnd_src_stop_gen2(mod);
- rsnd_dma_stop(rsnd_mod_to_dma(mod));
+ rsnd_dma_stop(io, rsnd_mod_to_dma(mod));
return ret;
}
-static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
+static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(src);
+ u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fsrate;
if (!runtime)
@@ -873,10 +897,10 @@ static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
}
static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -907,7 +931,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
/*
* enable sync convert
*/
- ret = rsnd_kctrl_new_s(mod, rtd,
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
rsnd_io_is_play(io) ?
"SRC Out Rate Switch" :
"SRC In Rate Switch",
@@ -916,7 +940,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- ret = rsnd_kctrl_new_s(mod, rtd,
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
rsnd_io_is_play(io) ?
"SRC Out Rate" :
"SRC In Rate",
@@ -1041,7 +1065,7 @@ int rsnd_src_probe(struct platform_device *pdev,
src->info = &info->src_info[i];
- ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
+ ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
if (ret)
return ret;
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 7bb9c087f3dc..2fbe59f7f9b5 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -66,6 +66,7 @@ struct rsnd_ssi {
u32 cr_own;
u32 cr_clk;
+ int chan;
int err;
unsigned int usrcnt;
};
@@ -80,16 +81,15 @@ struct rsnd_ssi {
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
-#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
+#define rsnd_ssi_parent(ssi) ((ssi)->parent)
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
#define rsnd_ssi_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
-int rsnd_ssi_use_busif(struct rsnd_mod *mod)
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int use_busif = 0;
if (!rsnd_ssi_is_dma_mode(mod))
@@ -189,22 +189,26 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
rsnd_mod_hw_start(&ssi->mod);
if (rsnd_rdai_is_clk_master(rdai)) {
- if (rsnd_ssi_clk_from_parent(ssi))
- rsnd_ssi_hw_start(ssi->parent, io);
+ struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+
+ if (ssi_parent)
+ rsnd_ssi_hw_start(ssi_parent, io);
else
rsnd_ssi_master_clk_start(ssi, io);
}
}
- cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
- DMEN : /* DMA : enable DMA */
- DIEN; /* PIO : enable Data interrupt */
-
+ if (rsnd_ssi_is_dma_mode(&ssi->mod)) {
+ cr_mode = UIEN | OIEN | /* over/under run */
+ DMEN; /* DMA : enable DMA */
+ } else {
+ cr_mode = DIEN; /* PIO : enable Data interrupt */
+ }
cr = ssi->cr_own |
ssi->cr_clk |
cr_mode |
- UIEN | OIEN | EN;
+ EN;
rsnd_mod_write(&ssi->mod, SSICR, cr);
@@ -221,16 +225,17 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
}
-static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
+static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 cr;
- if (0 == ssi->usrcnt) /* stop might be called without start */
+ if (0 == ssi->usrcnt) {
+ dev_err(dev, "%s called without starting\n", __func__);
return;
+ }
ssi->usrcnt--;
@@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
rsnd_ssi_status_check(&ssi->mod, IIRQ);
if (rsnd_rdai_is_clk_master(rdai)) {
- if (rsnd_ssi_clk_from_parent(ssi))
- rsnd_ssi_hw_stop(ssi->parent);
+ struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+
+ if (ssi_parent)
+ rsnd_ssi_hw_stop(io, ssi_parent);
else
rsnd_ssi_master_clk_stop(ssi);
}
rsnd_mod_hw_stop(&ssi->mod);
+
+ ssi->chan = 0;
}
dev_dbg(dev, "%s[%d] hw stopped\n",
@@ -270,10 +279,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
* SSI mod common functions
*/
static int rsnd_ssi_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 cr;
@@ -321,6 +330,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
}
static int rsnd_ssi_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@@ -336,6 +346,37 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
return 0;
}
+static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+ int chan = params_channels(params);
+
+ /*
+ * Already working.
+ * It will happen if SSI has parent/child connection.
+ */
+ if (ssi->usrcnt) {
+ /*
+ * it is error if child <-> parent SSI uses
+ * different channels.
+ */
+ if (ssi->chan != chan)
+ return -EIO;
+ }
+
+ /* It will be removed on rsnd_ssi_hw_stop */
+ ssi->chan = chan;
+ if (ssi_parent)
+ return rsnd_ssi_hw_params(&ssi_parent->mod, io,
+ substream, params);
+
+ return 0;
+}
+
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
{
/* under/over flow error */
@@ -348,12 +389,12 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
}
static int rsnd_ssi_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod));
+ rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod));
rsnd_ssi_hw_start(ssi, io);
@@ -363,6 +404,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod,
}
static int rsnd_ssi_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@@ -371,24 +413,29 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
- rsnd_ssi_hw_stop(ssi);
+ rsnd_ssi_hw_stop(io, ssi);
- rsnd_src_ssiu_stop(mod);
+ rsnd_src_ssiu_stop(mod, io);
return 0;
}
-static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
+static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
{
- struct rsnd_ssi *ssi = data;
- struct rsnd_mod *mod = &ssi->mod;
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_dma = rsnd_ssi_is_dma_mode(mod);
- u32 status = rsnd_mod_read(mod, SSISR);
+ u32 status;
+ bool elapsed = false;
+
+ spin_lock(&priv->lock);
+
+ /* ignore all cases if not working */
+ if (!rsnd_io_is_working(io))
+ goto rsnd_ssi_interrupt_out;
- if (!io)
- return IRQ_NONE;
+ status = rsnd_mod_read(mod, SSISR);
/* PIO only */
if (!is_dma && (status & DIRQ)) {
@@ -406,11 +453,11 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
else
*buf = rsnd_mod_read(mod, SSIRDR);
- rsnd_dai_pointer_update(io, sizeof(*buf));
+ elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
}
- /* PIO / DMA */
- if (status & (UIRQ | OIRQ)) {
+ /* DMA only */
+ if (is_dma && (status & (UIRQ | OIRQ))) {
struct device *dev = rsnd_priv_to_dev(priv);
/*
@@ -419,15 +466,28 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
dev_dbg(dev, "%s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
- rsnd_ssi_stop(mod, priv);
+ rsnd_ssi_stop(mod, io, priv);
if (ssi->err < 1024)
- rsnd_ssi_start(mod, priv);
+ rsnd_ssi_start(mod, io, priv);
else
dev_warn(dev, "no more SSI restart\n");
}
rsnd_ssi_record_error(ssi, status);
+rsnd_ssi_interrupt_out:
+ spin_unlock(&priv->lock);
+
+ if (elapsed)
+ rsnd_dai_period_elapsed(io);
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
+{
+ struct rsnd_mod *mod = data;
+
+ rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt);
+
return IRQ_HANDLED;
}
@@ -435,6 +495,7 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
* SSI PIO
*/
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
@@ -444,7 +505,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
ret = devm_request_irq(dev, ssi->info->irq,
rsnd_ssi_interrupt,
IRQF_SHARED,
- dev_name(dev), ssi);
+ dev_name(dev), mod);
return ret;
}
@@ -456,9 +517,11 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
+ .hw_params = rsnd_ssi_hw_params,
};
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@@ -469,25 +532,26 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
ret = devm_request_irq(dev, ssi->info->irq,
rsnd_ssi_interrupt,
IRQF_SHARED,
- dev_name(dev), ssi);
+ dev_name(dev), mod);
if (ret)
return ret;
ret = rsnd_dma_init(
- priv, rsnd_mod_to_dma(mod),
+ io, rsnd_mod_to_dma(mod),
dma_id);
return ret;
}
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
int irq = ssi->info->irq;
- rsnd_dma_quit(rsnd_mod_to_dma(mod));
+ rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
/* PIO will request IRQ again */
devm_free_irq(dev, irq, ssi);
@@ -496,6 +560,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
}
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
@@ -516,37 +581,39 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod,
}
static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- rsnd_dma_start(dma);
+ rsnd_dma_start(io, dma);
- rsnd_ssi_start(mod, priv);
+ rsnd_ssi_start(mod, io, priv);
return 0;
}
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- rsnd_ssi_stop(mod, priv);
+ rsnd_ssi_stop(mod, io, priv);
- rsnd_dma_stop(dma);
+ rsnd_dma_stop(io, dma);
return 0;
}
-static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod)
+static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_play = rsnd_io_is_play(io);
char *name;
- if (rsnd_ssi_use_busif(mod))
+ if (rsnd_ssi_use_busif(io, mod))
name = is_play ? "rxu" : "txu";
else
name = is_play ? "rx" : "tx";
@@ -565,6 +632,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.start = rsnd_ssi_dma_start,
.stop = rsnd_ssi_dma_stop,
.fallback = rsnd_ssi_fallback,
+ .hw_params = rsnd_ssi_hw_params,
};
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
@@ -598,7 +666,7 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
}
-static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
+static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
{
if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
return;
@@ -728,11 +796,11 @@ int rsnd_ssi_probe(struct platform_device *pdev,
else if (rsnd_ssi_pio_available(ssi))
ops = &rsnd_ssi_pio_ops;
- ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i);
+ ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i);
if (ret)
return ret;
- rsnd_ssi_parent_clk_setup(priv, ssi);
+ rsnd_ssi_parent_setup(priv, ssi);
}
return 0;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 23732523f87c..3a4a5c0e3f97 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -40,6 +40,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
+#include <sound/soc-topology.h>
#include <sound/initval.h>
#define CREATE_TRACE_POINTS
@@ -92,30 +93,21 @@ static int format_register_str(struct snd_soc_codec *codec,
int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
int regsize = codec->driver->reg_word_size * 2;
int ret;
- char tmpbuf[len + 1];
- char regbuf[regsize + 1];
-
- /* since tmpbuf is allocated on the stack, warn the callers if they
- * try to abuse this function */
- WARN_ON(len > 63);
/* +2 for ': ' and + 1 for '\n' */
if (wordsize + regsize + 2 + 1 != len)
return -EINVAL;
- ret = snd_soc_read(codec, reg);
- if (ret < 0) {
- memset(regbuf, 'X', regsize);
- regbuf[regsize] = '\0';
- } else {
- snprintf(regbuf, regsize + 1, "%.*x", regsize, ret);
- }
-
- /* prepare the buffer */
- snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf);
- /* copy it back to the caller without the '\0' */
- memcpy(buf, tmpbuf, len);
+ sprintf(buf, "%.*x: ", wordsize, reg);
+ buf += wordsize + 2;
+ ret = snd_soc_read(codec, reg);
+ if (ret < 0)
+ memset(buf, 'X', regsize);
+ else
+ sprintf(buf, "%.*x", regsize, ret);
+ buf[regsize] = '\n';
+ /* no NUL-termination needed */
return 0;
}
@@ -750,23 +742,10 @@ static void soc_resume_deferred(struct work_struct *work)
}
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- /* If the CODEC was idle over suspend then it will have been
- * left with bias OFF or STANDBY and suspended so we must now
- * resume. Otherwise the suspend was suppressed.
- */
if (codec->suspended) {
- switch (codec->dapm.bias_level) {
- case SND_SOC_BIAS_STANDBY:
- case SND_SOC_BIAS_OFF:
- if (codec->driver->resume)
- codec->driver->resume(codec);
- codec->suspended = 0;
- break;
- default:
- dev_dbg(codec->dev,
- "ASoC: CODEC was on over suspend\n");
- break;
- }
+ if (codec->driver->resume)
+ codec->driver->resume(codec);
+ codec->suspended = 0;
}
}
@@ -904,12 +883,17 @@ static struct snd_soc_dai *snd_soc_find_dai(
{
struct snd_soc_component *component;
struct snd_soc_dai *dai;
+ struct device_node *component_of_node;
lockdep_assert_held(&client_mutex);
/* Find CPU DAI from registered DAIs*/
list_for_each_entry(component, &component_list, list) {
- if (dlc->of_node && component->dev->of_node != dlc->of_node)
+ component_of_node = component->dev->of_node;
+ if (!component_of_node && component->dev->parent)
+ component_of_node = component->dev->parent->of_node;
+
+ if (dlc->of_node && component_of_node != dlc->of_node)
continue;
if (dlc->name && strcmp(component->name, dlc->name))
continue;
@@ -2435,6 +2419,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
card->rtd_aux[i].card = card;
INIT_LIST_HEAD(&card->dapm_dirty);
+ INIT_LIST_HEAD(&card->dobj_list);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
@@ -2599,7 +2584,8 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
* the same naming style even though those DAIs are not
* component-less anymore.
*/
- if (count == 1 && legacy_dai_naming) {
+ if (count == 1 && legacy_dai_naming &&
+ (dai_drv[i].id == 0 || dai_drv[i].name == NULL)) {
dai->name = fmt_single_name(dev, &dai->id);
} else {
dai->name = fmt_multiple_name(dev, &dai_drv[i]);
@@ -2749,6 +2735,7 @@ static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
}
list_add(&component->list, &component_list);
+ INIT_LIST_HEAD(&component->dobj_list);
}
static void snd_soc_component_add(struct snd_soc_component *component)
@@ -2825,6 +2812,7 @@ void snd_soc_unregister_component(struct device *dev)
return;
found:
+ snd_soc_tplg_component_remove(cmpnt, SND_SOC_TPLG_INDEX_ALL);
snd_soc_component_del_unlocked(cmpnt);
mutex_unlock(&client_mutex);
snd_soc_component_cleanup(cmpnt);
@@ -3488,11 +3476,16 @@ static int snd_soc_get_dai_name(struct of_phandle_args *args,
const char **dai_name)
{
struct snd_soc_component *pos;
+ struct device_node *component_of_node;
int ret = -EPROBE_DEFER;
mutex_lock(&client_mutex);
list_for_each_entry(pos, &component_list, list) {
- if (pos->dev->of_node != args->np)
+ component_of_node = pos->dev->of_node;
+ if (!component_of_node && pos->dev->parent)
+ component_of_node = pos->dev->parent->of_node;
+
+ if (component_of_node != args->np)
continue;
if (pos->driver->of_xlate_dai_name) {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 158204d08924..aa327c92480c 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,10 +52,15 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
const char *control,
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink));
-static struct snd_soc_dapm_widget *
+
+struct snd_soc_dapm_widget *
snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget);
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_widget *widget);
+
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
[snd_soc_dapm_pre] = 0,
@@ -70,6 +75,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_aif_out] = 4,
[snd_soc_dapm_mic] = 5,
[snd_soc_dapm_mux] = 6,
+ [snd_soc_dapm_demux] = 6,
[snd_soc_dapm_dac] = 7,
[snd_soc_dapm_switch] = 8,
[snd_soc_dapm_mixer] = 8,
@@ -100,6 +106,7 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_mic] = 7,
[snd_soc_dapm_micbias] = 8,
[snd_soc_dapm_mux] = 9,
+ [snd_soc_dapm_demux] = 9,
[snd_soc_dapm_aif_in] = 10,
[snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai_in] = 10,
@@ -308,14 +315,13 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
{
struct dapm_kcontrol_data *data;
struct soc_mixer_control *mc;
+ struct soc_enum *e;
+ const char *name;
+ int ret;
data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- dev_err(widget->dapm->dev,
- "ASoC: can't allocate kcontrol data for %s\n",
- widget->name);
+ if (!data)
return -ENOMEM;
- }
INIT_LIST_HEAD(&data->paths);
@@ -328,6 +334,13 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
if (mc->autodisable) {
struct snd_soc_dapm_widget template;
+ name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name,
+ "Autodisable");
+ if (!name) {
+ ret = -ENOMEM;
+ goto err_data;
+ }
+
memset(&template, 0, sizeof(template));
template.reg = mc->reg;
template.mask = (1 << fls(mc->max)) - 1;
@@ -338,16 +351,53 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
template.off_val = 0;
template.on_val = template.off_val;
template.id = snd_soc_dapm_kcontrol;
- template.name = kcontrol->id.name;
+ template.name = name;
data->value = template.on_val;
- data->widget = snd_soc_dapm_new_control(widget->dapm,
+ data->widget =
+ snd_soc_dapm_new_control_unlocked(widget->dapm,
&template);
if (!data->widget) {
- kfree(data);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_name;
+ }
+ }
+ break;
+ case snd_soc_dapm_demux:
+ case snd_soc_dapm_mux:
+ e = (struct soc_enum *)kcontrol->private_value;
+
+ if (e->autodisable) {
+ struct snd_soc_dapm_widget template;
+
+ name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name,
+ "Autodisable");
+ if (!name) {
+ ret = -ENOMEM;
+ goto err_data;
}
+
+ memset(&template, 0, sizeof(template));
+ template.reg = e->reg;
+ template.mask = e->mask << e->shift_l;
+ template.shift = e->shift_l;
+ template.off_val = snd_soc_enum_item_to_val(e, 0);
+ template.on_val = template.off_val;
+ template.id = snd_soc_dapm_kcontrol;
+ template.name = name;
+
+ data->value = template.on_val;
+
+ data->widget = snd_soc_dapm_new_control(widget->dapm,
+ &template);
+ if (!data->widget) {
+ ret = -ENOMEM;
+ goto err_name;
+ }
+
+ snd_soc_dapm_add_path(widget->dapm, data->widget,
+ widget, NULL, NULL);
}
break;
default:
@@ -357,11 +407,19 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
kcontrol->private_data = data;
return 0;
+
+err_name:
+ kfree(name);
+err_data:
+ kfree(data);
+ return ret;
}
static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
{
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl);
+ if (data->widget)
+ kfree(data->widget->name);
kfree(data->wlist);
kfree(data);
}
@@ -405,11 +463,6 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
list_add_tail(&path->list_kcontrol, &data->paths);
-
- if (data->widget) {
- snd_soc_dapm_add_path(data->widget->dapm, data->widget,
- path->source, NULL, NULL);
- }
}
static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol)
@@ -525,6 +578,67 @@ static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
snd_soc_component_async_complete(dapm->component);
}
+static struct snd_soc_dapm_widget *
+dapm_wcache_lookup(struct snd_soc_dapm_wcache *wcache, const char *name)
+{
+ struct snd_soc_dapm_widget *w = wcache->widget;
+ struct list_head *wlist;
+ const int depth = 2;
+ int i = 0;
+
+ if (w) {
+ wlist = &w->dapm->card->widgets;
+
+ list_for_each_entry_from(w, wlist, list) {
+ if (!strcmp(name, w->name))
+ return w;
+
+ if (++i == depth)
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static inline void dapm_wcache_update(struct snd_soc_dapm_wcache *wcache,
+ struct snd_soc_dapm_widget *w)
+{
+ wcache->widget = w;
+}
+
+/**
+ * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level
+ * @dapm: The DAPM context for which to set the level
+ * @level: The level to set
+ *
+ * Forces the DAPM bias level to a specific state. It will call the bias level
+ * callback of DAPM context with the specified level. This will even happen if
+ * the context is already at the same level. Furthermore it will not go through
+ * the normal bias level sequencing, meaning any intermediate states between the
+ * current and the target state will not be entered.
+ *
+ * Note that the change in bias level is only temporary and the next time
+ * snd_soc_dapm_sync() is called the state will be set to the level as
+ * determined by the DAPM core. The function is mainly intended to be used to
+ * used during probe or resume from suspend to power up the device so
+ * initialization can be done, before the DAPM core takes over.
+ */
+int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ int ret = 0;
+
+ if (dapm->set_bias_level)
+ ret = dapm->set_bias_level(dapm, level);
+
+ if (ret == 0)
+ dapm->bias_level = level;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level);
+
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
* @dapm: DAPM context
@@ -547,10 +661,8 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
if (ret != 0)
goto out;
- if (dapm->set_bias_level)
- ret = dapm->set_bias_level(dapm, level);
- else if (!card || dapm != &card->dapm)
- dapm->bias_level = level;
+ if (!card || dapm != &card->dapm)
+ ret = snd_soc_dapm_force_bias_level(dapm, level);
if (ret != 0)
goto out;
@@ -565,9 +677,10 @@ out:
/* connect mux widget to its interconnecting audio paths */
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_path *path, const char *control_name)
+ struct snd_soc_dapm_path *path, const char *control_name,
+ struct snd_soc_dapm_widget *w)
{
- const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
+ const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
int i;
@@ -707,6 +820,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
wname_in_long_name = false;
kcname_in_long_name = true;
break;
+ case snd_soc_dapm_demux:
case snd_soc_dapm_mux:
wname_in_long_name = true;
kcname_in_long_name = false;
@@ -777,6 +891,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
{
int i, ret;
struct snd_soc_dapm_path *path;
+ struct dapm_kcontrol_data *data;
/* add kcontrol */
for (i = 0; i < w->num_kcontrols; i++) {
@@ -786,16 +901,20 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
if (path->name != (char *)w->kcontrol_news[i].name)
continue;
- if (w->kcontrols[i]) {
- dapm_kcontrol_add_path(w->kcontrols[i], path);
- continue;
+ if (!w->kcontrols[i]) {
+ ret = dapm_create_or_share_mixmux_kcontrol(w, i);
+ if (ret < 0)
+ return ret;
}
- ret = dapm_create_or_share_mixmux_kcontrol(w, i);
- if (ret < 0)
- return ret;
-
dapm_kcontrol_add_path(w->kcontrols[i], path);
+
+ data = snd_kcontrol_chip(w->kcontrols[i]);
+ if (data->widget)
+ snd_soc_dapm_add_path(data->widget->dapm,
+ data->widget,
+ path->source,
+ NULL, NULL);
}
}
@@ -807,17 +926,32 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_context *dapm = w->dapm;
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;
+ type = "mux";
+ break;
+ case snd_soc_dapm_demux:
+ paths = &w->sinks;
+ type = "demux";
+ break;
+ default:
+ return -EINVAL;
+ }
+
if (w->num_kcontrols != 1) {
dev_err(dapm->dev,
- "ASoC: mux %s has incorrect number of controls\n",
+ "ASoC: %s %s has incorrect number of controls\n", type,
w->name);
return -EINVAL;
}
- if (list_empty(&w->sources)) {
- dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name);
+ if (list_empty(paths)) {
+ dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);
return -EINVAL;
}
@@ -825,9 +959,16 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
if (ret < 0)
return ret;
- list_for_each_entry(path, &w->sources, list_sink) {
- if (path->name)
- dapm_kcontrol_add_path(w->kcontrols[0], path);
+ 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);
+ }
}
return 0;
@@ -2335,6 +2476,50 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
}
}
+static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
+ const char *control)
+{
+ bool dynamic_source = false;
+ bool dynamic_sink = false;
+
+ if (!control)
+ return 0;
+
+ switch (source->id) {
+ case snd_soc_dapm_demux:
+ dynamic_source = true;
+ break;
+ default:
+ break;
+ }
+
+ switch (sink->id) {
+ case snd_soc_dapm_mux:
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ dynamic_sink = true;
+ break;
+ default:
+ break;
+ }
+
+ if (dynamic_source && dynamic_sink) {
+ dev_err(dapm->dev,
+ "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",
+ source->name, control, sink->name);
+ return -EINVAL;
+ } else if (!dynamic_source && !dynamic_sink) {
+ dev_err(dapm->dev,
+ "Control not supported for path %s -> [%s] -> %s\n",
+ source->name, control, sink->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
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,
@@ -2365,6 +2550,10 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}
+ ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
+ if (ret)
+ return ret;
+
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path)
return -ENOMEM;
@@ -2384,10 +2573,19 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
if (control == NULL) {
path->connect = 1;
} else {
- /* connect dynamic paths */
+ switch (wsource->id) {
+ case snd_soc_dapm_demux:
+ ret = dapm_connect_mux(dapm, path, control, wsource);
+ if (ret)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
switch (wsink->id) {
case snd_soc_dapm_mux:
- ret = dapm_connect_mux(dapm, path, control);
+ ret = dapm_connect_mux(dapm, path, control, wsink);
if (ret != 0)
goto err;
break;
@@ -2399,11 +2597,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
goto err;
break;
default:
- dev_err(dapm->dev,
- "Control not supported for path %s -> [%s] -> %s\n",
- wsource->name, control, wsink->name);
- ret = -EINVAL;
- goto err;
+ break;
}
}
@@ -2451,6 +2645,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
source = route->source;
}
+ wsource = dapm_wcache_lookup(&dapm->path_source_cache, source);
+ wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink);
+
+ if (wsink && wsource)
+ goto skip_search;
+
/*
* find src and dest widgets over all widgets but favor a widget from
* current DAPM context
@@ -2458,14 +2658,20 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
list_for_each_entry(w, &dapm->card->widgets, list) {
if (!wsink && !(strcmp(w->name, sink))) {
wtsink = w;
- if (w->dapm == dapm)
+ if (w->dapm == dapm) {
wsink = w;
+ if (wsource)
+ break;
+ }
continue;
}
if (!wsource && !(strcmp(w->name, source))) {
wtsource = w;
- if (w->dapm == dapm)
+ if (w->dapm == dapm) {
wsource = w;
+ if (wsink)
+ break;
+ }
}
}
/* use widget from another DAPM context if not found from this */
@@ -2485,6 +2691,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
return -ENODEV;
}
+skip_search:
+ dapm_wcache_update(&dapm->path_sink_cache, wsink);
+ dapm_wcache_update(&dapm->path_source_cache, wsource);
+
ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
route->connected);
if (ret)
@@ -2736,6 +2946,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
dapm_new_mixer(w);
break;
case snd_soc_dapm_mux:
+ case snd_soc_dapm_demux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
@@ -2902,16 +3113,21 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_card *card = dapm->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val;
- if (e->reg != SND_SOC_NOPM) {
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) {
int ret = soc_dapm_read(dapm, e->reg, &reg_val);
- if (ret)
+ if (ret) {
+ mutex_unlock(&card->dapm_mutex);
return ret;
+ }
} else {
reg_val = dapm_kcontrol_get_value(kcontrol);
}
+ mutex_unlock(&card->dapm_mutex);
val = (reg_val >> e->shift_l) & e->mask;
ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
@@ -2941,7 +3157,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_soc_card *card = dapm->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
- unsigned int val, change;
+ unsigned int val, change, reg_change = 0;
unsigned int mask;
struct snd_soc_dapm_update update;
int ret = 0;
@@ -2960,19 +3176,20 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ change = dapm_kcontrol_set_value(kcontrol, val);
+
if (e->reg != SND_SOC_NOPM)
- change = soc_dapm_test_bits(dapm, e->reg, mask, val);
- else
- change = dapm_kcontrol_set_value(kcontrol, val);
+ reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val);
- if (change) {
- if (e->reg != SND_SOC_NOPM) {
+ if (change || reg_change) {
+ if (reg_change) {
update.kcontrol = kcontrol;
update.reg = e->reg;
update.mask = mask;
update.val = val;
card->update = &update;
}
+ change |= reg_change;
ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);
@@ -3053,8 +3270,25 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
-static struct snd_soc_dapm_widget *
+struct snd_soc_dapm_widget *
snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_widget *widget)
+{
+ struct snd_soc_dapm_widget *w;
+
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+ if (!w)
+ dev_err(dapm->dev,
+ "ASoC: Failed to create DAPM control %s\n",
+ widget->name);
+
+ mutex_unlock(&dapm->card->dapm_mutex);
+ return w;
+}
+
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
@@ -3141,6 +3375,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
w->power_check = dapm_always_on_check_power;
break;
case snd_soc_dapm_mux:
+ case snd_soc_dapm_demux:
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
@@ -3174,7 +3409,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
- list_add(&w->list, &dapm->card->widgets);
+ list_add_tail(&w->list, &dapm->card->widgets);
w->inputs = -1;
w->outputs = -1;
@@ -3204,7 +3439,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
- w = snd_soc_dapm_new_control(dapm, widget);
+ w = snd_soc_dapm_new_control_unlocked(dapm, widget);
if (!w) {
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s\n",
@@ -3442,7 +3677,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
- w = snd_soc_dapm_new_control(&card->dapm, &template);
+ w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
if (!w) {
dev_err(card->dev, "ASoC: Failed to create %s widget\n",
link_name);
@@ -3493,7 +3728,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
dev_dbg(dai->dev, "ASoC: adding %s widget\n",
template.name);
- w = snd_soc_dapm_new_control(dapm, &template);
+ w = snd_soc_dapm_new_control_unlocked(dapm, &template);
if (!w) {
dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
dai->driver->playback.stream_name);
@@ -3512,7 +3747,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
dev_dbg(dai->dev, "ASoC: adding %s widget\n",
template.name);
- w = snd_soc_dapm_new_control(dapm, &template);
+ w = snd_soc_dapm_new_control_unlocked(dapm, &template);
if (!w) {
dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
dai->driver->capture.stream_name);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index c9917ca5de1a..6fd1906af387 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -24,6 +24,12 @@
#include <sound/dmaengine_pcm.h>
+/*
+ * The platforms dmaengine driver does not support reporting the amount of
+ * bytes that are still left to transfer.
+ */
+#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31)
+
struct dmaengine_pcm {
struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1];
const struct snd_dmaengine_pcm_config *config;
@@ -222,14 +228,18 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
}
-static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan)
+static bool dmaengine_pcm_can_report_residue(struct device *dev,
+ struct dma_chan *chan)
{
struct dma_slave_caps dma_caps;
int ret;
ret = dma_get_slave_caps(chan, &dma_caps);
- if (ret != 0)
- return true;
+ if (ret != 0) {
+ dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
+ ret);
+ return false;
+ }
if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
return false;
@@ -289,14 +299,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (ret)
return ret;
- /*
- * This will only return false if we know for sure that at least
- * one channel does not support residue reporting. If the DMA
- * driver does not implement the slave_caps API we rely having
- * the NO_RESIDUE flag set manually in case residue reporting is
- * not supported.
- */
- if (!dmaengine_pcm_can_report_residue(pcm->chan[i]))
+ if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i]))
pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
}
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 9f60c25c4568..fbaa1bb41102 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -48,7 +48,7 @@ int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
INIT_LIST_HEAD(&jack->jack_zones);
BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
- ret = snd_jack_new(card->snd_card, id, type, &jack->jack);
+ ret = snd_jack_new(card->snd_card, id, type, &jack->jack, false, false);
if (ret)
return ret;
@@ -197,6 +197,7 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
INIT_LIST_HEAD(&pins[i].list);
list_add(&(pins[i].list), &jack->pins);
+ snd_jack_add_new_kctl(jack->jack, pins[i].pin, pins[i].mask);
}
/* Update to reflect the last reported status; canned jack
@@ -315,8 +316,11 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
goto undo;
}
- if (gpios[i].gpiod_dev) {
- /* GPIO descriptor */
+ if (gpios[i].desc) {
+ /* Already have a GPIO descriptor. */
+ goto got_gpio;
+ } else if (gpios[i].gpiod_dev) {
+ /* Get a GPIO descriptor */
gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
gpios[i].name,
gpios[i].idx, GPIOD_IN);
@@ -344,7 +348,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
gpios[i].desc = gpio_to_desc(gpios[i].gpio);
}
-
+got_gpio:
INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
gpios[i].jack = jack;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 35fe58f4fa86..256b9c91aa94 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1485,30 +1485,67 @@ unwind:
}
static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
- struct snd_soc_pcm_stream *stream)
+ struct snd_soc_pcm_stream *stream,
+ u64 formats)
{
runtime->hw.rate_min = stream->rate_min;
runtime->hw.rate_max = stream->rate_max;
runtime->hw.channels_min = stream->channels_min;
runtime->hw.channels_max = stream->channels_max;
if (runtime->hw.formats)
- runtime->hw.formats &= stream->formats;
+ runtime->hw.formats &= formats & stream->formats;
else
- runtime->hw.formats = stream->formats;
+ runtime->hw.formats = formats & stream->formats;
runtime->hw.rates = stream->rates;
}
+static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ struct snd_soc_dpcm *dpcm;
+ u64 formats = ULLONG_MAX;
+ int stream = substream->stream;
+
+ if (!fe->dai_link->dpcm_merged_format)
+ return formats;
+
+ /*
+ * It returns merged BE codec format
+ * if FE want to use it (= dpcm_merged_format)
+ */
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_soc_dai_driver *codec_dai_drv;
+ struct snd_soc_pcm_stream *codec_stream;
+ int i;
+
+ for (i = 0; i < be->num_codecs; i++) {
+ codec_dai_drv = be->codec_dais[i]->driver;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &codec_dai_drv->playback;
+ else
+ codec_stream = &codec_dai_drv->capture;
+
+ formats &= codec_stream->formats;
+ }
+ }
+
+ return formats;
+}
+
static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
+ u64 format = dpcm_runtime_base_format(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
+ dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format);
else
- dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
+ dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format);
}
static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
new file mode 100644
index 000000000000..d0960683c409
--- /dev/null
+++ b/sound/soc/soc-topology.c
@@ -0,0 +1,1826 @@
+/*
+ * soc-topology.c -- ALSA SoC Topology
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2015 Intel Corporation.
+ *
+ * Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+ * K, Mythri P <mythri.p.k@intel.com>
+ * Prusty, Subhransu S <subhransu.s.prusty@intel.com>
+ * B, Jayachandran <jayachandran.b@intel.com>
+ * Abdullah, Omair M <omair.m.abdullah@intel.com>
+ * Jin, Yao <yao.jin@intel.com>
+ * Lin, Mengdong <mengdong.lin@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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Add support to read audio firmware topology alongside firmware text. The
+ * topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links,
+ * equalizers, firmware, coefficients etc.
+ *
+ * This file only manages the core ALSA and ASoC components, all other bespoke
+ * firmware topology data is passed to component drivers for bespoke handling.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-topology.h>
+
+/*
+ * We make several passes over the data (since it wont necessarily be ordered)
+ * and process objects in the following order. This guarantees the component
+ * drivers will be ready with any vendor data before the mixers and DAPM objects
+ * are loaded (that may make use of the vendor data).
+ */
+#define SOC_TPLG_PASS_MANIFEST 0
+#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_START SOC_TPLG_PASS_MANIFEST
+#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PCM_DAI
+
+struct soc_tplg {
+ const struct firmware *fw;
+
+ /* runtime FW parsing */
+ const u8 *pos; /* read postion */
+ const u8 *hdr_pos; /* header position */
+ unsigned int pass; /* pass number */
+
+ /* component caller */
+ struct device *dev;
+ struct snd_soc_component *comp;
+ u32 index; /* current block index */
+ u32 req_index; /* required index, only loaded/free matching blocks */
+
+ /* kcontrol operations */
+ const struct snd_soc_tplg_kcontrol_ops *io_ops;
+ int io_ops_count;
+
+ /* optional fw loading callbacks to component drivers */
+ struct snd_soc_tplg_ops *ops;
+};
+
+static int soc_tplg_process_headers(struct soc_tplg *tplg);
+static void soc_tplg_complete(struct soc_tplg *tplg);
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_widget *widget);
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_widget *widget);
+
+/* check we dont overflow the data for this control chunk */
+static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
+ unsigned int count, size_t bytes, const char *elem_type)
+{
+ const u8 *end = tplg->pos + elem_size * count;
+
+ if (end > tplg->fw->data + tplg->fw->size) {
+ dev_err(tplg->dev, "ASoC: %s overflow end of data\n",
+ elem_type);
+ return -EINVAL;
+ }
+
+ /* check there is enough room in chunk for control.
+ extra bytes at the end of control are for vendor data here */
+ if (elem_size * count > bytes) {
+ dev_err(tplg->dev,
+ "ASoC: %s count %d of size %zu is bigger than chunk %zu\n",
+ elem_type, count, elem_size, bytes);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int soc_tplg_is_eof(struct soc_tplg *tplg)
+{
+ const u8 *end = tplg->hdr_pos;
+
+ if (end >= tplg->fw->data + tplg->fw->size)
+ return 1;
+ return 0;
+}
+
+static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg)
+{
+ return (unsigned long)(tplg->hdr_pos - tplg->fw->data);
+}
+
+static inline unsigned long soc_tplg_get_offset(struct soc_tplg *tplg)
+{
+ return (unsigned long)(tplg->pos - tplg->fw->data);
+}
+
+/* mapping of Kcontrol types and associated operations. */
+static const struct snd_soc_tplg_kcontrol_ops io_ops[] = {
+ {SND_SOC_TPLG_CTL_VOLSW, snd_soc_get_volsw,
+ snd_soc_put_volsw, snd_soc_info_volsw},
+ {SND_SOC_TPLG_CTL_VOLSW_SX, snd_soc_get_volsw_sx,
+ snd_soc_put_volsw_sx, NULL},
+ {SND_SOC_TPLG_CTL_ENUM, snd_soc_get_enum_double,
+ snd_soc_put_enum_double, snd_soc_info_enum_double},
+ {SND_SOC_TPLG_CTL_ENUM_VALUE, snd_soc_get_enum_double,
+ snd_soc_put_enum_double, NULL},
+ {SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get,
+ snd_soc_bytes_put, snd_soc_bytes_info},
+ {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range,
+ snd_soc_put_volsw_range, snd_soc_info_volsw_range},
+ {SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx,
+ snd_soc_put_xr_sx, snd_soc_info_xr_sx},
+ {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe,
+ snd_soc_put_strobe, NULL},
+ {SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw,
+ snd_soc_dapm_put_volsw, NULL},
+ {SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double,
+ snd_soc_dapm_put_enum_double, snd_soc_info_enum_double},
+ {SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double,
+ snd_soc_dapm_put_enum_double, NULL},
+ {SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE, snd_soc_dapm_get_enum_double,
+ snd_soc_dapm_put_enum_double, NULL},
+ {SND_SOC_TPLG_DAPM_CTL_PIN, snd_soc_dapm_get_pin_switch,
+ snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch},
+};
+
+struct soc_tplg_map {
+ int uid;
+ int kid;
+};
+
+/* mapping of widget types from UAPI IDs to kernel IDs */
+static const struct soc_tplg_map dapm_map[] = {
+ {SND_SOC_TPLG_DAPM_INPUT, snd_soc_dapm_input},
+ {SND_SOC_TPLG_DAPM_OUTPUT, snd_soc_dapm_output},
+ {SND_SOC_TPLG_DAPM_MUX, snd_soc_dapm_mux},
+ {SND_SOC_TPLG_DAPM_MIXER, snd_soc_dapm_mixer},
+ {SND_SOC_TPLG_DAPM_PGA, snd_soc_dapm_pga},
+ {SND_SOC_TPLG_DAPM_OUT_DRV, snd_soc_dapm_out_drv},
+ {SND_SOC_TPLG_DAPM_ADC, snd_soc_dapm_adc},
+ {SND_SOC_TPLG_DAPM_DAC, snd_soc_dapm_dac},
+ {SND_SOC_TPLG_DAPM_SWITCH, snd_soc_dapm_switch},
+ {SND_SOC_TPLG_DAPM_PRE, snd_soc_dapm_pre},
+ {SND_SOC_TPLG_DAPM_POST, snd_soc_dapm_post},
+ {SND_SOC_TPLG_DAPM_AIF_IN, snd_soc_dapm_aif_in},
+ {SND_SOC_TPLG_DAPM_AIF_OUT, snd_soc_dapm_aif_out},
+ {SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in},
+ {SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out},
+ {SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link},
+};
+
+static int tplc_chan_get_reg(struct soc_tplg *tplg,
+ struct snd_soc_tplg_channel *chan, int map)
+{
+ int i;
+
+ for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) {
+ if (chan[i].id == map)
+ return chan[i].reg;
+ }
+
+ return -EINVAL;
+}
+
+static int tplc_chan_get_shift(struct soc_tplg *tplg,
+ struct snd_soc_tplg_channel *chan, int map)
+{
+ int i;
+
+ for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) {
+ if (chan[i].id == map)
+ return chan[i].shift;
+ }
+
+ return -EINVAL;
+}
+
+static int get_widget_id(int tplg_type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dapm_map); i++) {
+ if (tplg_type == dapm_map[i].uid)
+ return dapm_map[i].kid;
+ }
+
+ return -EINVAL;
+}
+
+static enum snd_soc_dobj_type get_dobj_mixer_type(
+ struct snd_soc_tplg_ctl_hdr *control_hdr)
+{
+ if (control_hdr == NULL)
+ return SND_SOC_DOBJ_NONE;
+
+ switch (control_hdr->ops.info) {
+ case SND_SOC_TPLG_CTL_VOLSW:
+ case SND_SOC_TPLG_CTL_VOLSW_SX:
+ case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+ case SND_SOC_TPLG_CTL_RANGE:
+ case SND_SOC_TPLG_CTL_STROBE:
+ return SND_SOC_DOBJ_MIXER;
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ return SND_SOC_DOBJ_ENUM;
+ case SND_SOC_TPLG_CTL_BYTES:
+ return SND_SOC_DOBJ_BYTES;
+ default:
+ return SND_SOC_DOBJ_NONE;
+ }
+}
+
+static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr,
+ struct snd_soc_tplg_ctl_hdr *control_hdr)
+{
+ switch (hdr->type) {
+ case SND_SOC_TPLG_TYPE_MIXER:
+ return get_dobj_mixer_type(control_hdr);
+ case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
+ case SND_SOC_TPLG_TYPE_MANIFEST:
+ return SND_SOC_DOBJ_NONE;
+ case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
+ return SND_SOC_DOBJ_WIDGET;
+ case SND_SOC_TPLG_TYPE_DAI_LINK:
+ return SND_SOC_DOBJ_DAI_LINK;
+ case SND_SOC_TPLG_TYPE_PCM:
+ return SND_SOC_DOBJ_PCM;
+ case SND_SOC_TPLG_TYPE_CODEC_LINK:
+ return SND_SOC_DOBJ_CODEC_LINK;
+ default:
+ return SND_SOC_DOBJ_NONE;
+ }
+}
+
+static inline void soc_bind_err(struct soc_tplg *tplg,
+ struct snd_soc_tplg_ctl_hdr *hdr, int index)
+{
+ dev_err(tplg->dev,
+ "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n",
+ hdr->ops.get, hdr->ops.put, hdr->ops.info, index,
+ soc_tplg_get_offset(tplg));
+}
+
+static inline void soc_control_err(struct soc_tplg *tplg,
+ struct snd_soc_tplg_ctl_hdr *hdr, const char *name)
+{
+ dev_err(tplg->dev,
+ "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n",
+ name, hdr->ops.get, hdr->ops.put, hdr->ops.info,
+ soc_tplg_get_offset(tplg));
+}
+
+/* pass vendor data to component driver for processing */
+static int soc_tplg_vendor_load_(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ int ret = 0;
+
+ if (tplg->comp && tplg->ops && tplg->ops->vendor_load)
+ ret = tplg->ops->vendor_load(tplg->comp, hdr);
+ else {
+ dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n",
+ hdr->vendor_type);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ dev_err(tplg->dev,
+ "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n",
+ soc_tplg_get_hdr_offset(tplg),
+ soc_tplg_get_hdr_offset(tplg),
+ hdr->type, hdr->vendor_type);
+ return ret;
+}
+
+/* pass vendor data to component driver for processing */
+static int soc_tplg_vendor_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ if (tplg->pass != SOC_TPLG_PASS_VENDOR)
+ return 0;
+
+ return soc_tplg_vendor_load_(tplg, hdr);
+}
+
+/* optionally pass new dynamic widget to component driver. This is mainly for
+ * external widgets where we can assign private data/ops */
+static int soc_tplg_widget_load(struct soc_tplg *tplg,
+ struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ if (tplg->comp && tplg->ops && tplg->ops->widget_load)
+ return tplg->ops->widget_load(tplg->comp, w, tplg_w);
+
+ return 0;
+}
+
+/* pass dynamic FEs configurations to component driver */
+static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai)
+{
+ if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load)
+ return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai);
+
+ return 0;
+}
+
+/* tell the component driver that all firmware has been loaded in this request */
+static void soc_tplg_complete(struct soc_tplg *tplg)
+{
+ if (tplg->comp && tplg->ops && tplg->ops->complete)
+ tplg->ops->complete(tplg->comp);
+}
+
+/* add a dynamic kcontrol */
+static int soc_tplg_add_dcontrol(struct snd_card *card, struct device *dev,
+ const struct snd_kcontrol_new *control_new, const char *prefix,
+ void *data, struct snd_kcontrol **kcontrol)
+{
+ int err;
+
+ *kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix);
+ if (*kcontrol == NULL) {
+ dev_err(dev, "ASoC: Failed to create new kcontrol %s\n",
+ control_new->name);
+ return -ENOMEM;
+ }
+
+ err = snd_ctl_add(card, *kcontrol);
+ if (err < 0) {
+ dev_err(dev, "ASoC: Failed to add %s: %d\n",
+ control_new->name, err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* add a dynamic kcontrol for component driver */
+static int soc_tplg_add_kcontrol(struct soc_tplg *tplg,
+ struct snd_kcontrol_new *k, struct snd_kcontrol **kcontrol)
+{
+ struct snd_soc_component *comp = tplg->comp;
+
+ return soc_tplg_add_dcontrol(comp->card->snd_card,
+ comp->dev, k, NULL, comp, kcontrol);
+}
+
+/* remove a mixer kcontrol */
+static void remove_mixer(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_card *card = comp->card->snd_card;
+ struct soc_mixer_control *sm =
+ container_of(dobj, struct soc_mixer_control, dobj);
+ const unsigned int *p = NULL;
+
+ if (pass != SOC_TPLG_PASS_MIXER)
+ return;
+
+ if (dobj->ops && dobj->ops->control_unload)
+ dobj->ops->control_unload(comp, dobj);
+
+ if (sm->dobj.control.kcontrol->tlv.p)
+ p = sm->dobj.control.kcontrol->tlv.p;
+ snd_ctl_remove(card, sm->dobj.control.kcontrol);
+ list_del(&sm->dobj.list);
+ kfree(sm);
+ kfree(p);
+}
+
+/* remove an enum kcontrol */
+static void remove_enum(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_card *card = comp->card->snd_card;
+ struct soc_enum *se = container_of(dobj, struct soc_enum, dobj);
+ int i;
+
+ if (pass != SOC_TPLG_PASS_MIXER)
+ return;
+
+ if (dobj->ops && dobj->ops->control_unload)
+ dobj->ops->control_unload(comp, dobj);
+
+ snd_ctl_remove(card, se->dobj.control.kcontrol);
+ list_del(&se->dobj.list);
+
+ kfree(se->dobj.control.dvalues);
+ for (i = 0; i < se->items; i++)
+ kfree(se->dobj.control.dtexts[i]);
+ kfree(se);
+}
+
+/* remove a byte kcontrol */
+static void remove_bytes(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_card *card = comp->card->snd_card;
+ struct soc_bytes_ext *sb =
+ container_of(dobj, struct soc_bytes_ext, dobj);
+
+ if (pass != SOC_TPLG_PASS_MIXER)
+ return;
+
+ if (dobj->ops && dobj->ops->control_unload)
+ dobj->ops->control_unload(comp, dobj);
+
+ snd_ctl_remove(card, sb->dobj.control.kcontrol);
+ list_del(&sb->dobj.list);
+ kfree(sb);
+}
+
+/* remove a widget and it's kcontrols - routes must be removed first */
+static void remove_widget(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_card *card = comp->card->snd_card;
+ struct snd_soc_dapm_widget *w =
+ container_of(dobj, struct snd_soc_dapm_widget, dobj);
+ int i;
+
+ if (pass != SOC_TPLG_PASS_WIDGET)
+ return;
+
+ if (dobj->ops && dobj->ops->widget_unload)
+ dobj->ops->widget_unload(comp, dobj);
+
+ /*
+ * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers.
+ * The enum may either have an array of values or strings.
+ */
+ if (dobj->widget.kcontrol_enum) {
+ /* enumerated widget mixer */
+ struct soc_enum *se =
+ (struct soc_enum *)w->kcontrols[0]->private_value;
+
+ snd_ctl_remove(card, w->kcontrols[0]);
+
+ kfree(se->dobj.control.dvalues);
+ for (i = 0; i < se->items; i++)
+ kfree(se->dobj.control.dtexts[i]);
+
+ kfree(se);
+ kfree(w->kcontrol_news);
+ } else {
+ /* non enumerated widget mixer */
+ for (i = 0; i < w->num_kcontrols; i++) {
+ struct snd_kcontrol *kcontrol = w->kcontrols[i];
+ struct soc_mixer_control *sm =
+ (struct soc_mixer_control *) kcontrol->private_value;
+
+ kfree(w->kcontrols[i]->tlv.p);
+
+ snd_ctl_remove(card, w->kcontrols[i]);
+ kfree(sm);
+ }
+ kfree(w->kcontrol_news);
+ }
+ /* widget w is freed by soc-dapm.c */
+}
+
+/* remove PCM DAI configurations */
+static void remove_pcm_dai(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ if (pass != SOC_TPLG_PASS_PCM_DAI)
+ return;
+
+ if (dobj->ops && dobj->ops->pcm_dai_unload)
+ dobj->ops->pcm_dai_unload(comp, dobj);
+
+ list_del(&dobj->list);
+ kfree(dobj);
+}
+
+/* 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)
+{
+ int i;
+
+ /* try and map standard kcontrols handler first */
+ for (i = 0; i < num_ops; i++) {
+
+ if (ops[i].id == hdr->ops.put)
+ k->put = ops[i].put;
+ if (ops[i].id == hdr->ops.get)
+ k->get = ops[i].get;
+ if (ops[i].id == hdr->ops.info)
+ k->info = ops[i].info;
+ }
+
+ /* standard handlers found ? */
+ if (k->put && k->get && k->info)
+ return 0;
+
+ /* none found so try bespoke handlers */
+ for (i = 0; i < num_bops; 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 && ops[i].id == hdr->ops.info)
+ k->info = bops[i].info;
+ }
+
+ /* bespoke handlers found ? */
+ if (k->put && k->get && k->info)
+ return 0;
+
+ /* nothing to bind */
+ return -EINVAL;
+}
+
+/* bind a widgets to it's evnt handlers */
+int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w,
+ const struct snd_soc_tplg_widget_events *events,
+ int num_events, u16 event_type)
+{
+ int i;
+
+ w->event = NULL;
+
+ for (i = 0; i < num_events; i++) {
+ if (event_type == events[i].type) {
+
+ /* found - so assign event */
+ w->event = events[i].event_handler;
+ return 0;
+ }
+ }
+
+ /* not found */
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event);
+
+/* optionally pass new dynamic kcontrol to component driver. */
+static int soc_tplg_init_kcontrol(struct soc_tplg *tplg,
+ struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ if (tplg->comp && tplg->ops && tplg->ops->control_load)
+ return tplg->ops->control_load(tplg->comp, k, hdr);
+
+ return 0;
+}
+
+static int soc_tplg_create_tlv(struct soc_tplg *tplg,
+ struct snd_kcontrol_new *kc, u32 tlv_size)
+{
+ struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+ struct snd_ctl_tlv *tlv;
+
+ if (tlv_size == 0)
+ return 0;
+
+ tplg_tlv = (struct snd_soc_tplg_ctl_tlv *) tplg->pos;
+ tplg->pos += tlv_size;
+
+ tlv = kzalloc(sizeof(*tlv) + tlv_size, GFP_KERNEL);
+ if (tlv == NULL)
+ return -ENOMEM;
+
+ dev_dbg(tplg->dev, " created TLV type %d size %d bytes\n",
+ tplg_tlv->numid, tplg_tlv->size);
+
+ tlv->numid = tplg_tlv->numid;
+ tlv->length = tplg_tlv->size;
+ memcpy(tlv->tlv, tplg_tlv + 1, tplg_tlv->size);
+ kc->tlv.p = (void *)tlv;
+
+ return 0;
+}
+
+static inline void soc_tplg_free_tlv(struct soc_tplg *tplg,
+ struct snd_kcontrol_new *kc)
+{
+ kfree(kc->tlv.p);
+}
+
+static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
+ size_t size)
+{
+ struct snd_soc_tplg_bytes_control *be;
+ struct soc_bytes_ext *sbe;
+ struct snd_kcontrol_new kc;
+ int i, err;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_bytes_control), count,
+ size, "mixer bytes")) {
+ dev_err(tplg->dev, "ASoC: Invalid count %d for byte control\n",
+ count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+
+ /* validate kcontrol */
+ if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+
+ sbe = kzalloc(sizeof(*sbe), GFP_KERNEL);
+ if (sbe == NULL)
+ return -ENOMEM;
+
+ tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+ be->priv.size);
+
+ dev_dbg(tplg->dev,
+ "ASoC: adding bytes kcontrol %s with access 0x%x\n",
+ be->hdr.name, be->hdr.access);
+
+ memset(&kc, 0, sizeof(kc));
+ kc.name = be->hdr.name;
+ kc.private_value = (long)sbe;
+ kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc.access = be->hdr.access;
+
+ sbe->max = be->max;
+ sbe->dobj.type = SND_SOC_DOBJ_BYTES;
+ sbe->dobj.ops = tplg->ops;
+ 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);
+ if (err) {
+ soc_control_err(tplg, &be->hdr, be->hdr.name);
+ kfree(sbe);
+ continue;
+ }
+
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, &kc,
+ (struct snd_soc_tplg_ctl_hdr *)be);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ be->hdr.name);
+ kfree(sbe);
+ continue;
+ }
+
+ /* register control here */
+ err = soc_tplg_add_kcontrol(tplg, &kc,
+ &sbe->dobj.control.kcontrol);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to add %s\n",
+ be->hdr.name);
+ kfree(sbe);
+ continue;
+ }
+
+ list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+ }
+ return 0;
+
+}
+
+static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
+ size_t size)
+{
+ struct snd_soc_tplg_mixer_control *mc;
+ struct soc_mixer_control *sm;
+ struct snd_kcontrol_new kc;
+ int i, err;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_mixer_control),
+ count, size, "mixers")) {
+
+ dev_err(tplg->dev, "ASoC: invalid count %d for controls\n",
+ count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
+
+ /* validate kcontrol */
+ if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+
+ sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+ if (sm == NULL)
+ return -ENOMEM;
+ tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+ mc->priv.size);
+
+ dev_dbg(tplg->dev,
+ "ASoC: adding mixer kcontrol %s with access 0x%x\n",
+ mc->hdr.name, mc->hdr.access);
+
+ memset(&kc, 0, sizeof(kc));
+ kc.name = mc->hdr.name;
+ kc.private_value = (long)sm;
+ kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc.access = mc->hdr.access;
+
+ /* we only support FL/FR channel mapping atm */
+ sm->reg = tplc_chan_get_reg(tplg, mc->channel,
+ SNDRV_CHMAP_FL);
+ sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
+ SNDRV_CHMAP_FR);
+ sm->shift = tplc_chan_get_shift(tplg, mc->channel,
+ SNDRV_CHMAP_FL);
+ sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
+ SNDRV_CHMAP_FR);
+
+ sm->max = mc->max;
+ sm->min = mc->min;
+ sm->invert = mc->invert;
+ sm->platform_max = mc->platform_max;
+ sm->dobj.index = tplg->index;
+ sm->dobj.ops = tplg->ops;
+ sm->dobj.type = SND_SOC_DOBJ_MIXER;
+ 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);
+ if (err) {
+ soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+ kfree(sm);
+ continue;
+ }
+
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, &kc,
+ (struct snd_soc_tplg_ctl_hdr *) mc);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ mc->hdr.name);
+ kfree(sm);
+ continue;
+ }
+
+ /* create any TLV data */
+ soc_tplg_create_tlv(tplg, &kc, mc->hdr.tlv_size);
+
+ /* register control here */
+ err = soc_tplg_add_kcontrol(tplg, &kc,
+ &sm->dobj.control.kcontrol);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to add %s\n",
+ mc->hdr.name);
+ soc_tplg_free_tlv(tplg, &kc);
+ kfree(sm);
+ continue;
+ }
+
+ list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+ }
+
+ return 0;
+}
+
+static int soc_tplg_denum_create_texts(struct soc_enum *se,
+ struct snd_soc_tplg_enum_control *ec)
+{
+ int i, ret;
+
+ se->dobj.control.dtexts =
+ kzalloc(sizeof(char *) * ec->items, GFP_KERNEL);
+ if (se->dobj.control.dtexts == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ec->items; i++) {
+
+ if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ se->dobj.control.dtexts[i] = kstrdup(ec->texts[i], GFP_KERNEL);
+ if (!se->dobj.control.dtexts[i]) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ for (--i; i >= 0; i--)
+ kfree(se->dobj.control.dtexts[i]);
+ kfree(se->dobj.control.dtexts);
+ return ret;
+}
+
+static int soc_tplg_denum_create_values(struct soc_enum *se,
+ struct snd_soc_tplg_enum_control *ec)
+{
+ if (ec->items > sizeof(*ec->values))
+ return -EINVAL;
+
+ se->dobj.control.dvalues =
+ kmalloc(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;
+}
+
+static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
+ size_t size)
+{
+ struct snd_soc_tplg_enum_control *ec;
+ struct soc_enum *se;
+ struct snd_kcontrol_new kc;
+ int i, ret, err;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_enum_control),
+ count, size, "enums")) {
+
+ dev_err(tplg->dev, "ASoC: invalid count %d for enum controls\n",
+ count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ ec->priv.size);
+
+ /* validate kcontrol */
+ if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+
+ se = kzalloc((sizeof(*se)), GFP_KERNEL);
+ if (se == NULL)
+ return -ENOMEM;
+
+ dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
+ ec->hdr.name, ec->items);
+
+ memset(&kc, 0, sizeof(kc));
+ kc.name = ec->hdr.name;
+ kc.private_value = (long)se;
+ kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc.access = ec->hdr.access;
+
+ se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+ SNDRV_CHMAP_FL);
+ se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+ SNDRV_CHMAP_FL);
+
+ se->items = ec->items;
+ se->mask = ec->mask;
+ se->dobj.index = tplg->index;
+ se->dobj.type = SND_SOC_DOBJ_ENUM;
+ se->dobj.ops = tplg->ops;
+ INIT_LIST_HEAD(&se->dobj.list);
+
+ switch (ec->hdr.ops.info) {
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ err = soc_tplg_denum_create_values(se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev,
+ "ASoC: could not create values for %s\n",
+ ec->hdr.name);
+ kfree(se);
+ continue;
+ }
+ /* fall through and create texts */
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ err = soc_tplg_denum_create_texts(se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev,
+ "ASoC: could not create texts for %s\n",
+ ec->hdr.name);
+ kfree(se);
+ continue;
+ }
+ break;
+ default:
+ dev_err(tplg->dev,
+ "ASoC: invalid enum control type %d for %s\n",
+ ec->hdr.ops.info, ec->hdr.name);
+ kfree(se);
+ continue;
+ }
+
+ /* 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);
+ if (err) {
+ soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+ kfree(se);
+ continue;
+ }
+
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, &kc,
+ (struct snd_soc_tplg_ctl_hdr *) ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ ec->hdr.name);
+ kfree(se);
+ continue;
+ }
+
+ /* register control here */
+ ret = soc_tplg_add_kcontrol(tplg,
+ &kc, &se->dobj.control.kcontrol);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n",
+ ec->hdr.name);
+ kfree(se);
+ continue;
+ }
+
+ list_add(&se->dobj.list, &tplg->comp->dobj_list);
+ }
+
+ return 0;
+}
+
+static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_tplg_ctl_hdr *control_hdr;
+ int i;
+
+ if (tplg->pass != SOC_TPLG_PASS_MIXER) {
+ tplg->pos += hdr->size + hdr->payload_size;
+ return 0;
+ }
+
+ dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count,
+ soc_tplg_get_offset(tplg));
+
+ for (i = 0; i < hdr->count; i++) {
+
+ control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
+
+ switch (control_hdr->ops.info) {
+ case SND_SOC_TPLG_CTL_VOLSW:
+ case SND_SOC_TPLG_CTL_STROBE:
+ case SND_SOC_TPLG_CTL_VOLSW_SX:
+ case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+ case SND_SOC_TPLG_CTL_RANGE:
+ case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+ case SND_SOC_TPLG_DAPM_CTL_PIN:
+ soc_tplg_dmixer_create(tplg, 1, hdr->payload_size);
+ break;
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ soc_tplg_denum_create(tplg, 1, hdr->payload_size);
+ break;
+ case SND_SOC_TPLG_CTL_BYTES:
+ soc_tplg_dbytes_create(tplg, 1, hdr->payload_size);
+ break;
+ default:
+ soc_bind_err(tplg, control_hdr, i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_dapm_context *dapm = &tplg->comp->dapm;
+ struct snd_soc_dapm_route route;
+ struct snd_soc_tplg_dapm_graph_elem *elem;
+ int count = hdr->count, i;
+
+ if (tplg->pass != SOC_TPLG_PASS_GRAPH) {
+ tplg->pos += hdr->size + hdr->payload_size;
+ return 0;
+ }
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_dapm_graph_elem),
+ count, hdr->payload_size, "graph")) {
+
+ dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n",
+ count);
+ return -EINVAL;
+ }
+
+ dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes\n", count);
+
+ for (i = 0; i < count; i++) {
+ elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos;
+ tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem);
+
+ /* validate routes */
+ if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+ if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+ if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+
+ route.source = elem->source;
+ route.sink = elem->sink;
+ route.connected = NULL; /* set to NULL atm for tplg users */
+ if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0)
+ route.control = NULL;
+ else
+ route.control = elem->control;
+
+ /* add route, but keep going if some fail */
+ snd_soc_dapm_add_routes(dapm, &route, 1);
+ }
+
+ return 0;
+}
+
+static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
+ struct soc_tplg *tplg, int num_kcontrols)
+{
+ struct snd_kcontrol_new *kc;
+ struct soc_mixer_control *sm;
+ struct snd_soc_tplg_mixer_control *mc;
+ int i, err;
+
+ kc = kzalloc(sizeof(*kc) * num_kcontrols, GFP_KERNEL);
+ if (kc == NULL)
+ return NULL;
+
+ for (i = 0; i < num_kcontrols; i++) {
+ mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
+ sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+ if (sm == NULL)
+ goto err;
+
+ tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+ mc->priv.size);
+
+ /* validate kcontrol */
+ if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ goto err_str;
+
+ dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n",
+ mc->hdr.name, i);
+
+ kc[i].name = mc->hdr.name;
+ kc[i].private_value = (long)sm;
+ kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc[i].access = mc->hdr.access;
+
+ /* we only support FL/FR channel mapping atm */
+ sm->reg = tplc_chan_get_reg(tplg, mc->channel,
+ SNDRV_CHMAP_FL);
+ sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
+ SNDRV_CHMAP_FR);
+ sm->shift = tplc_chan_get_shift(tplg, mc->channel,
+ SNDRV_CHMAP_FL);
+ sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
+ SNDRV_CHMAP_FR);
+
+ sm->max = mc->max;
+ sm->min = mc->min;
+ sm->invert = mc->invert;
+ sm->platform_max = mc->platform_max;
+ sm->dobj.index = tplg->index;
+ 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);
+ if (err) {
+ soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+ kfree(sm);
+ continue;
+ }
+
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, &kc[i],
+ (struct snd_soc_tplg_ctl_hdr *)mc);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ mc->hdr.name);
+ kfree(sm);
+ continue;
+ }
+ }
+ return kc;
+
+err_str:
+ kfree(sm);
+err:
+ for (--i; i >= 0; i--)
+ kfree((void *)kc[i].private_value);
+ kfree(kc);
+ return NULL;
+}
+
+static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
+ struct soc_tplg *tplg)
+{
+ struct snd_kcontrol_new *kc;
+ struct snd_soc_tplg_enum_control *ec;
+ struct soc_enum *se;
+ int i, err;
+
+ ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ ec->priv.size);
+
+ /* validate kcontrol */
+ if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return NULL;
+
+ kc = kzalloc(sizeof(*kc), GFP_KERNEL);
+ if (kc == NULL)
+ return NULL;
+
+ se = kzalloc(sizeof(*se), GFP_KERNEL);
+ if (se == NULL)
+ goto err;
+
+ dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
+ ec->hdr.name);
+
+ kc->name = ec->hdr.name;
+ kc->private_value = (long)se;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = ec->hdr.access;
+
+ /* we only support FL/FR channel mapping atm */
+ se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR);
+
+ se->items = ec->items;
+ se->mask = ec->mask;
+ se->dobj.index = tplg->index;
+
+ switch (ec->hdr.ops.info) {
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ err = soc_tplg_denum_create_values(se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: could not create values for %s\n",
+ ec->hdr.name);
+ goto err_se;
+ }
+ /* fall through to create texts */
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ err = soc_tplg_denum_create_texts(se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
+ ec->hdr.name);
+ goto err_se;
+ }
+ break;
+ default:
+ dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
+ ec->hdr.ops.info, ec->hdr.name);
+ goto err_se;
+ }
+
+ /* 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);
+ if (err) {
+ soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+ goto err_se;
+ }
+
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, kc,
+ (struct snd_soc_tplg_ctl_hdr *)ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ ec->hdr.name);
+ goto err_se;
+ }
+
+ return kc;
+
+err_se:
+ /* free values and texts */
+ kfree(se->dobj.control.dvalues);
+ for (i = 0; i < ec->items; i++)
+ kfree(se->dobj.control.dtexts[i]);
+
+ kfree(se);
+err:
+ kfree(kc);
+
+ return NULL;
+}
+
+static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create(
+ struct soc_tplg *tplg, int count)
+{
+ struct snd_soc_tplg_bytes_control *be;
+ struct soc_bytes_ext *sbe;
+ struct snd_kcontrol_new *kc;
+ int i, err;
+
+ kc = kzalloc(sizeof(*kc) * count, GFP_KERNEL);
+ if (!kc)
+ return NULL;
+
+ for (i = 0; i < count; i++) {
+ be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+
+ /* validate kcontrol */
+ if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ goto err;
+
+ sbe = kzalloc(sizeof(*sbe), GFP_KERNEL);
+ if (sbe == NULL)
+ goto err;
+
+ tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+ be->priv.size);
+
+ dev_dbg(tplg->dev,
+ "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;
+ kc[i].access = be->hdr.access;
+
+ sbe->max = be->max;
+ 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);
+ if (err) {
+ soc_control_err(tplg, &be->hdr, be->hdr.name);
+ kfree(sbe);
+ continue;
+ }
+
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, &kc[i],
+ (struct snd_soc_tplg_ctl_hdr *)be);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ be->hdr.name);
+ kfree(sbe);
+ continue;
+ }
+ }
+
+ return kc;
+
+err:
+ for (--i; i >= 0; i--)
+ kfree((void *)kc[i].private_value);
+
+ kfree(kc);
+ return NULL;
+}
+
+static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
+ struct snd_soc_tplg_dapm_widget *w)
+{
+ struct snd_soc_dapm_context *dapm = &tplg->comp->dapm;
+ struct snd_soc_dapm_widget template, *widget;
+ struct snd_soc_tplg_ctl_hdr *control_hdr;
+ struct snd_soc_card *card = tplg->comp->card;
+ int ret = 0;
+
+ if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+ if (strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
+
+ dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n",
+ w->name, w->id);
+
+ memset(&template, 0, sizeof(template));
+
+ /* map user to kernel widget ID */
+ template.id = get_widget_id(w->id);
+ if (template.id < 0)
+ return template.id;
+
+ template.name = kstrdup(w->name, GFP_KERNEL);
+ if (!template.name)
+ return -ENOMEM;
+ template.sname = kstrdup(w->sname, GFP_KERNEL);
+ if (!template.sname) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ template.reg = w->reg;
+ template.shift = w->shift;
+ template.mask = w->mask;
+ template.on_val = w->invert ? 0 : 1;
+ template.off_val = w->invert ? 1 : 0;
+ template.ignore_suspend = w->ignore_suspend;
+ template.event_flags = w->event_flags;
+ template.dobj.index = tplg->index;
+
+ tplg->pos +=
+ (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size);
+ if (w->num_kcontrols == 0) {
+ template.num_kcontrols = 0;
+ goto widget;
+ }
+
+ control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
+ dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n",
+ w->name, w->num_kcontrols, control_hdr->type);
+
+ switch (control_hdr->ops.info) {
+ case SND_SOC_TPLG_CTL_VOLSW:
+ case SND_SOC_TPLG_CTL_STROBE:
+ case SND_SOC_TPLG_CTL_VOLSW_SX:
+ case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+ case SND_SOC_TPLG_CTL_RANGE:
+ case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+ template.num_kcontrols = w->num_kcontrols;
+ template.kcontrol_news =
+ soc_tplg_dapm_widget_dmixer_create(tplg,
+ template.num_kcontrols);
+ if (!template.kcontrol_news) {
+ ret = -ENOMEM;
+ goto hdr_err;
+ }
+ break;
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ template.dobj.widget.kcontrol_enum = 1;
+ template.num_kcontrols = 1;
+ template.kcontrol_news =
+ soc_tplg_dapm_widget_denum_create(tplg);
+ if (!template.kcontrol_news) {
+ ret = -ENOMEM;
+ goto hdr_err;
+ }
+ break;
+ case SND_SOC_TPLG_CTL_BYTES:
+ template.num_kcontrols = w->num_kcontrols;
+ template.kcontrol_news =
+ soc_tplg_dapm_widget_dbytes_create(tplg,
+ template.num_kcontrols);
+ if (!template.kcontrol_news) {
+ ret = -ENOMEM;
+ goto hdr_err;
+ }
+ break;
+ default:
+ dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n",
+ control_hdr->ops.get, control_hdr->ops.put,
+ control_hdr->ops.info);
+ ret = -EINVAL;
+ goto hdr_err;
+ }
+
+widget:
+ ret = soc_tplg_widget_load(tplg, &template, w);
+ if (ret < 0)
+ goto hdr_err;
+
+ /* card dapm mutex is held by the core if we are loading topology
+ * data during sound card init. */
+ if (card->instantiated)
+ widget = snd_soc_dapm_new_control(dapm, &template);
+ else
+ widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
+ if (widget == NULL) {
+ dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
+ w->name);
+ goto hdr_err;
+ }
+
+ widget->dobj.type = SND_SOC_DOBJ_WIDGET;
+ widget->dobj.ops = tplg->ops;
+ widget->dobj.index = tplg->index;
+ list_add(&widget->dobj.list, &tplg->comp->dobj_list);
+ return 0;
+
+hdr_err:
+ kfree(template.sname);
+err:
+ kfree(template.name);
+ return ret;
+}
+
+static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_tplg_dapm_widget *widget;
+ int ret, count = hdr->count, i;
+
+ if (tplg->pass != SOC_TPLG_PASS_WIDGET)
+ return 0;
+
+ dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count);
+
+ for (i = 0; i < count; i++) {
+ widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos;
+ ret = soc_tplg_dapm_widget_create(tplg, widget);
+ if (ret < 0)
+ dev_err(tplg->dev, "ASoC: failed to load widget %s\n",
+ widget->name);
+ }
+
+ return 0;
+}
+
+static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
+{
+ struct snd_soc_card *card = tplg->comp->card;
+ int ret;
+
+ /* Card might not have been registered at this point.
+ * If so, just return success.
+ */
+ if (!card || !card->instantiated) {
+ dev_warn(tplg->dev, "ASoC: Parent card not yet available,"
+ "Do not add new widgets now\n");
+ return 0;
+ }
+
+ ret = snd_soc_dapm_new_widgets(card);
+ if (ret < 0)
+ dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n",
+ ret);
+
+ return 0;
+}
+
+static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct snd_soc_dobj *dobj;
+ int count = hdr->count;
+ int ret;
+
+ if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
+ return 0;
+
+ pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_pcm_dai), count,
+ hdr->payload_size, "PCM DAI")) {
+ dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n",
+ count);
+ return -EINVAL;
+ }
+
+ dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
+ tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count;
+
+ dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL);
+ if (dobj == NULL)
+ return -ENOMEM;
+
+ /* Call the platform driver call back to register the dais */
+ ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n");
+ goto err;
+ }
+
+ dobj->type = get_dobj_type(hdr, NULL);
+ dobj->pcm_dai.count = count;
+ dobj->pcm_dai.pd = pcm_dai;
+ dobj->ops = tplg->ops;
+ dobj->index = tplg->index;
+ list_add(&dobj->list, &tplg->comp->dobj_list);
+ return 0;
+
+err:
+ kfree(dobj);
+ return ret;
+}
+
+static int soc_tplg_manifest_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_tplg_manifest *manifest;
+
+ if (tplg->pass != SOC_TPLG_PASS_MANIFEST)
+ return 0;
+
+ manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
+ tplg->pos += sizeof(struct snd_soc_tplg_manifest);
+
+ if (tplg->comp && tplg->ops && tplg->ops->manifest)
+ return tplg->ops->manifest(tplg->comp, manifest);
+
+ dev_err(tplg->dev, "ASoC: Firmware manifest not supported\n");
+ return 0;
+}
+
+/* validate header magic, size and type */
+static int soc_valid_header(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
+ return 0;
+
+ /* big endian firmware objects not supported atm */
+ if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) {
+ dev_err(tplg->dev,
+ "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n",
+ tplg->pass, hdr->magic,
+ soc_tplg_get_hdr_offset(tplg), tplg->fw->size);
+ return -EINVAL;
+ }
+
+ if (hdr->magic != SND_SOC_TPLG_MAGIC) {
+ dev_err(tplg->dev,
+ "ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n",
+ tplg->pass, hdr->magic,
+ soc_tplg_get_hdr_offset(tplg), tplg->fw->size);
+ return -EINVAL;
+ }
+
+ if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) {
+ dev_err(tplg->dev,
+ "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n",
+ tplg->pass, hdr->abi,
+ SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg),
+ tplg->fw->size);
+ return -EINVAL;
+ }
+
+ if (hdr->payload_size == 0) {
+ dev_err(tplg->dev, "ASoC: header has 0 size at offset 0x%lx.\n",
+ soc_tplg_get_hdr_offset(tplg));
+ return -EINVAL;
+ }
+
+ if (tplg->pass == hdr->type)
+ dev_dbg(tplg->dev,
+ "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n",
+ hdr->payload_size, hdr->type, hdr->version,
+ hdr->vendor_type, tplg->pass);
+
+ return 1;
+}
+
+/* check header type and call appropriate handler */
+static int soc_tplg_load_header(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr);
+
+ /* check for matching ID */
+ if (hdr->index != tplg->req_index &&
+ hdr->index != SND_SOC_TPLG_INDEX_ALL)
+ return 0;
+
+ tplg->index = hdr->index;
+
+ switch (hdr->type) {
+ case SND_SOC_TPLG_TYPE_MIXER:
+ case SND_SOC_TPLG_TYPE_ENUM:
+ case SND_SOC_TPLG_TYPE_BYTES:
+ return soc_tplg_kcontrol_elems_load(tplg, hdr);
+ case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
+ return soc_tplg_dapm_graph_elems_load(tplg, hdr);
+ case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
+ return soc_tplg_dapm_widget_elems_load(tplg, hdr);
+ case SND_SOC_TPLG_TYPE_PCM:
+ case SND_SOC_TPLG_TYPE_DAI_LINK:
+ case SND_SOC_TPLG_TYPE_CODEC_LINK:
+ return soc_tplg_pcm_dai_elems_load(tplg, hdr);
+ case SND_SOC_TPLG_TYPE_MANIFEST:
+ return soc_tplg_manifest_load(tplg, hdr);
+ default:
+ /* bespoke vendor data object */
+ return soc_tplg_vendor_load(tplg, hdr);
+ }
+
+ return 0;
+}
+
+/* process the topology file headers */
+static int soc_tplg_process_headers(struct soc_tplg *tplg)
+{
+ struct snd_soc_tplg_hdr *hdr;
+ int ret;
+
+ tplg->pass = SOC_TPLG_PASS_START;
+
+ /* process the header types from start to end */
+ while (tplg->pass <= SOC_TPLG_PASS_END) {
+
+ tplg->hdr_pos = tplg->fw->data;
+ hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
+
+ while (!soc_tplg_is_eof(tplg)) {
+
+ /* make sure header is valid before loading */
+ ret = soc_valid_header(tplg, hdr);
+ if (ret < 0)
+ return ret;
+ else if (ret == 0)
+ break;
+
+ /* load the header object */
+ ret = soc_tplg_load_header(tplg, hdr);
+ if (ret < 0)
+ return ret;
+
+ /* goto next header */
+ tplg->hdr_pos += hdr->payload_size +
+ sizeof(struct snd_soc_tplg_hdr);
+ hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
+ }
+
+ /* next data type pass */
+ tplg->pass++;
+ }
+
+ /* signal DAPM we are complete */
+ ret = soc_tplg_dapm_complete(tplg);
+ if (ret < 0)
+ dev_err(tplg->dev,
+ "ASoC: failed to initialise DAPM from Firmware\n");
+
+ return ret;
+}
+
+static int soc_tplg_load(struct soc_tplg *tplg)
+{
+ int ret;
+
+ ret = soc_tplg_process_headers(tplg);
+ if (ret == 0)
+ soc_tplg_complete(tplg);
+
+ return ret;
+}
+
+/* load audio component topology from "firmware" file */
+int snd_soc_tplg_component_load(struct snd_soc_component *comp,
+ struct snd_soc_tplg_ops *ops, const struct firmware *fw, u32 id)
+{
+ struct soc_tplg tplg;
+
+ /* setup parsing context */
+ memset(&tplg, 0, sizeof(tplg));
+ tplg.fw = fw;
+ tplg.dev = comp->dev;
+ tplg.comp = comp;
+ tplg.ops = ops;
+ tplg.req_index = id;
+ tplg.io_ops = ops->io_ops;
+ tplg.io_ops_count = ops->io_ops_count;
+
+ return soc_tplg_load(&tplg);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
+
+/* remove this dynamic widget */
+void snd_soc_tplg_widget_remove(struct snd_soc_dapm_widget *w)
+{
+ /* make sure we are a widget */
+ if (w->dobj.type != SND_SOC_DOBJ_WIDGET)
+ return;
+
+ remove_widget(w->dapm->component, &w->dobj, SOC_TPLG_PASS_WIDGET);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove);
+
+/* remove all dynamic widgets from this DAPM context */
+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) {
+
+ /* make sure we are a widget with correct context */
+ if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm)
+ continue;
+
+ /* match ID */
+ 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);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all);
+
+/* remove dynamic controls from the component driver */
+int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index)
+{
+ struct snd_soc_dobj *dobj, *next_dobj;
+ int pass = SOC_TPLG_PASS_END;
+
+ /* process the header types from end to start */
+ while (pass >= SOC_TPLG_PASS_START) {
+
+ /* remove mixer controls */
+ list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list,
+ list) {
+
+ /* match index */
+ if (dobj->index != index &&
+ dobj->index != SND_SOC_TPLG_INDEX_ALL)
+ continue;
+
+ switch (dobj->type) {
+ case SND_SOC_DOBJ_MIXER:
+ remove_mixer(comp, dobj, pass);
+ break;
+ case SND_SOC_DOBJ_ENUM:
+ remove_enum(comp, dobj, pass);
+ break;
+ case SND_SOC_DOBJ_BYTES:
+ remove_bytes(comp, dobj, pass);
+ break;
+ case SND_SOC_DOBJ_WIDGET:
+ remove_widget(comp, dobj, pass);
+ break;
+ case SND_SOC_DOBJ_PCM:
+ case SND_SOC_DOBJ_DAI_LINK:
+ case SND_SOC_DOBJ_CODEC_LINK:
+ remove_pcm_dai(comp, dobj, pass);
+ break;
+ default:
+ dev_err(comp->dev, "ASoC: invalid component type %d for removal\n",
+ dobj->type);
+ break;
+ }
+ }
+ pass--;
+ }
+
+ /* let caller know if FW can be freed when no objects are left */
+ return !list_empty(&comp->dobj_list);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_component_remove);
diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c
index b81a7a4c938b..85d810d7667c 100644
--- a/sound/soc/ux500/mop500_ab8500.c
+++ b/sound/soc/ux500/mop500_ab8500.c
@@ -372,6 +372,10 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd)
/* Create driver private-data struct */
drvdata = devm_kzalloc(dev, sizeof(struct mop500_ab8500_drvdata),
GFP_KERNEL);
+
+ if (!drvdata)
+ return -ENOMEM;
+
snd_soc_card_set_drvdata(rtd->card, drvdata);
/* Setup clocks */
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index 51a66a87305a..f12c01dddc8d 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -147,7 +147,6 @@ int ux500_pcm_register_platform(struct platform_device *pdev)
pcm_config = &ux500_dmaengine_pcm_config;
ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config,
- SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_COMPAT);
if (ret < 0) {
dev_err(&pdev->dev,
diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig
new file mode 100644
index 000000000000..c47eb25e441f
--- /dev/null
+++ b/sound/soc/zte/Kconfig
@@ -0,0 +1,17 @@
+config ZX296702_SPDIF
+ tristate "ZX296702 spdif"
+ depends on SOC_ZX296702 || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to the
+ zx296702 spdif interface
+
+config ZX296702_I2S
+ tristate "ZX296702 i2s"
+ depends on SOC_ZX296702 || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to the
+ zx296702 i2s interface
diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile
new file mode 100644
index 000000000000..254ed2c8c1a0
--- /dev/null
+++ b/sound/soc/zte/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_ZX296702_SPDIF) += zx296702-spdif.o
+obj-$(CONFIG_ZX296702_I2S) += zx296702-i2s.o
diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx296702-i2s.c
new file mode 100644
index 000000000000..98d96e1b17e0
--- /dev/null
+++ b/sound/soc/zte/zx296702-i2s.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2015 Linaro
+ *
+ * Author: Jun Nie <jun.nie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define ZX_I2S_PROCESS_CTRL 0x04
+#define ZX_I2S_TIMING_CTRL 0x08
+#define ZX_I2S_FIFO_CTRL 0x0C
+#define ZX_I2S_FIFO_STATUS 0x10
+#define ZX_I2S_INT_EN 0x14
+#define ZX_I2S_INT_STATUS 0x18
+#define ZX_I2S_DATA 0x1C
+#define ZX_I2S_FRAME_CNTR 0x20
+
+#define I2S_DEAGULT_FIFO_THRES (0x10)
+#define I2S_MAX_FIFO_THRES (0x20)
+
+#define ZX_I2S_PROCESS_TX_EN (1 << 0)
+#define ZX_I2S_PROCESS_TX_DIS (0 << 0)
+#define ZX_I2S_PROCESS_RX_EN (1 << 1)
+#define ZX_I2S_PROCESS_RX_DIS (0 << 1)
+#define ZX_I2S_PROCESS_I2S_EN (1 << 2)
+#define ZX_I2S_PROCESS_I2S_DIS (0 << 2)
+
+#define ZX_I2S_TIMING_MAST (1 << 0)
+#define ZX_I2S_TIMING_SLAVE (0 << 0)
+#define ZX_I2S_TIMING_MS_MASK (1 << 0)
+#define ZX_I2S_TIMING_LOOP (1 << 1)
+#define ZX_I2S_TIMING_NOR (0 << 1)
+#define ZX_I2S_TIMING_LOOP_MASK (1 << 1)
+#define ZX_I2S_TIMING_PTNR (1 << 2)
+#define ZX_I2S_TIMING_NTPR (0 << 2)
+#define ZX_I2S_TIMING_PHASE_MASK (1 << 2)
+#define ZX_I2S_TIMING_TDM (1 << 3)
+#define ZX_I2S_TIMING_I2S (0 << 3)
+#define ZX_I2S_TIMING_TIMING_MASK (1 << 3)
+#define ZX_I2S_TIMING_LONG_SYNC (1 << 4)
+#define ZX_I2S_TIMING_SHORT_SYNC (0 << 4)
+#define ZX_I2S_TIMING_SYNC_MASK (1 << 4)
+#define ZX_I2S_TIMING_TEAK_EN (1 << 5)
+#define ZX_I2S_TIMING_TEAK_DIS (0 << 5)
+#define ZX_I2S_TIMING_TEAK_MASK (1 << 5)
+#define ZX_I2S_TIMING_STD_I2S (0 << 6)
+#define ZX_I2S_TIMING_MSB_JUSTIF (1 << 6)
+#define ZX_I2S_TIMING_LSB_JUSTIF (2 << 6)
+#define ZX_I2S_TIMING_ALIGN_MASK (3 << 6)
+#define ZX_I2S_TIMING_CHN_MASK (7 << 8)
+#define ZX_I2S_TIMING_CHN(x) ((x - 1) << 8)
+#define ZX_I2S_TIMING_LANE_MASK (3 << 11)
+#define ZX_I2S_TIMING_LANE(x) ((x - 1) << 11)
+#define ZX_I2S_TIMING_TSCFG_MASK (7 << 13)
+#define ZX_I2S_TIMING_TSCFG(x) (x << 13)
+#define ZX_I2S_TIMING_TS_WIDTH_MASK (0x1f << 16)
+#define ZX_I2S_TIMING_TS_WIDTH(x) ((x - 1) << 16)
+#define ZX_I2S_TIMING_DATA_SIZE_MASK (0x1f << 21)
+#define ZX_I2S_TIMING_DATA_SIZE(x) ((x - 1) << 21)
+#define ZX_I2S_TIMING_CFG_ERR_MASK (1 << 31)
+
+#define ZX_I2S_FIFO_CTRL_TX_RST (1 << 0)
+#define ZX_I2S_FIFO_CTRL_TX_RST_MASK (1 << 0)
+#define ZX_I2S_FIFO_CTRL_RX_RST (1 << 1)
+#define ZX_I2S_FIFO_CTRL_RX_RST_MASK (1 << 1)
+#define ZX_I2S_FIFO_CTRL_TX_DMA_EN (1 << 4)
+#define ZX_I2S_FIFO_CTRL_TX_DMA_DIS (0 << 4)
+#define ZX_I2S_FIFO_CTRL_TX_DMA_MASK (1 << 4)
+#define ZX_I2S_FIFO_CTRL_RX_DMA_EN (1 << 5)
+#define ZX_I2S_FIFO_CTRL_RX_DMA_DIS (0 << 5)
+#define ZX_I2S_FIFO_CTRL_RX_DMA_MASK (1 << 5)
+#define ZX_I2S_FIFO_CTRL_TX_THRES_MASK (0x1F << 8)
+#define ZX_I2S_FIFO_CTRL_RX_THRES_MASK (0x1F << 16)
+
+#define CLK_RAT (32 * 4)
+
+struct zx_i2s_info {
+ struct snd_dmaengine_dai_dma_data dma_playback;
+ struct snd_dmaengine_dai_dma_data dma_capture;
+ struct clk *dai_clk;
+ void __iomem *reg_base;
+ int master;
+ resource_size_t mapbase;
+};
+
+static void zx_i2s_tx_en(void __iomem *base, bool on)
+{
+ unsigned long val;
+
+ val = readl_relaxed(base + ZX_I2S_PROCESS_CTRL);
+ if (on)
+ val |= ZX_I2S_PROCESS_TX_EN | ZX_I2S_PROCESS_I2S_EN;
+ else
+ val &= ~(ZX_I2S_PROCESS_TX_EN | ZX_I2S_PROCESS_I2S_EN);
+ writel_relaxed(val, base + ZX_I2S_PROCESS_CTRL);
+}
+
+static void zx_i2s_rx_en(void __iomem *base, bool on)
+{
+ unsigned long val;
+
+ val = readl_relaxed(base + ZX_I2S_PROCESS_CTRL);
+ if (on)
+ val |= ZX_I2S_PROCESS_RX_EN | ZX_I2S_PROCESS_I2S_EN;
+ else
+ val &= ~(ZX_I2S_PROCESS_RX_EN | ZX_I2S_PROCESS_I2S_EN);
+ writel_relaxed(val, base + ZX_I2S_PROCESS_CTRL);
+}
+
+static void zx_i2s_tx_dma_en(void __iomem *base, bool on)
+{
+ unsigned long val;
+
+ val = readl_relaxed(base + ZX_I2S_FIFO_CTRL);
+ val |= ZX_I2S_FIFO_CTRL_TX_RST | (I2S_DEAGULT_FIFO_THRES << 8);
+ if (on)
+ val |= ZX_I2S_FIFO_CTRL_TX_DMA_EN;
+ else
+ val &= ~ZX_I2S_FIFO_CTRL_TX_DMA_EN;
+ writel_relaxed(val, base + ZX_I2S_FIFO_CTRL);
+}
+
+static void zx_i2s_rx_dma_en(void __iomem *base, bool on)
+{
+ unsigned long val;
+
+ val = readl_relaxed(base + ZX_I2S_FIFO_CTRL);
+ val |= ZX_I2S_FIFO_CTRL_RX_RST | (I2S_DEAGULT_FIFO_THRES << 16);
+ if (on)
+ val |= ZX_I2S_FIFO_CTRL_RX_DMA_EN;
+ else
+ val &= ~ZX_I2S_FIFO_CTRL_RX_DMA_EN;
+ writel_relaxed(val, base + ZX_I2S_FIFO_CTRL);
+}
+
+#define ZX_I2S_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000| \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define ZX_I2S_FMTBIT \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int zx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+
+ snd_soc_dai_set_drvdata(dai, zx_i2s);
+ zx_i2s->dma_playback.addr = zx_i2s->mapbase + ZX_I2S_DATA;
+ zx_i2s->dma_playback.maxburst = 16;
+ zx_i2s->dma_capture.addr = zx_i2s->mapbase + ZX_I2S_DATA;
+ zx_i2s->dma_capture.maxburst = 16;
+ snd_soc_dai_init_dma_data(dai, &zx_i2s->dma_playback,
+ &zx_i2s->dma_capture);
+ return 0;
+}
+
+static int zx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned long val;
+
+ val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL);
+ val &= ~(ZX_I2S_TIMING_TIMING_MASK | ZX_I2S_TIMING_ALIGN_MASK |
+ ZX_I2S_TIMING_TEAK_MASK | ZX_I2S_TIMING_SYNC_MASK |
+ ZX_I2S_TIMING_MS_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_STD_I2S);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_MSB_JUSTIF);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_LSB_JUSTIF);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unknown i2s timeing\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2s->master = 1;
+ val |= ZX_I2S_TIMING_MAST;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s->master = 0;
+ val |= ZX_I2S_TIMING_SLAVE;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unknown master/slave format\n");
+ return -EINVAL;
+ }
+
+ writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL);
+ return 0;
+}
+
+static int zx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *socdai)
+{
+ struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(socdai);
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ unsigned int lane, ch_num, len, ret = 0;
+ unsigned long val, format;
+ unsigned long chn_cfg;
+
+ dma_data = snd_soc_dai_get_dma_data(socdai, substream);
+ dma_data->addr_width = params_width(params) >> 3;
+
+ val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL);
+ val &= ~(ZX_I2S_TIMING_TS_WIDTH_MASK | ZX_I2S_TIMING_DATA_SIZE_MASK |
+ ZX_I2S_TIMING_LANE_MASK | ZX_I2S_TIMING_CHN_MASK |
+ ZX_I2S_TIMING_TSCFG_MASK);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ format = 0;
+ len = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ format = 1;
+ len = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ format = 2;
+ len = 32;
+ break;
+ default:
+ dev_err(socdai->dev, "Unknown data format\n");
+ return -EINVAL;
+ }
+ val |= ZX_I2S_TIMING_TS_WIDTH(len) | ZX_I2S_TIMING_DATA_SIZE(len);
+
+ ch_num = params_channels(params);
+ switch (ch_num) {
+ case 1:
+ lane = 1;
+ chn_cfg = 2;
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ lane = ch_num / 2;
+ chn_cfg = 3;
+ break;
+ default:
+ dev_err(socdai->dev, "Not support channel num %d\n", ch_num);
+ return -EINVAL;
+ }
+ val |= ZX_I2S_TIMING_LANE(lane);
+ val |= ZX_I2S_TIMING_TSCFG(chn_cfg);
+ val |= ZX_I2S_TIMING_CHN(ch_num);
+ writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL);
+
+ if (i2s->master)
+ ret = clk_set_rate(i2s->dai_clk,
+ params_rate(params) * ch_num * CLK_RAT);
+ return ret;
+}
+
+static int zx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+ int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (capture)
+ zx_i2s_rx_dma_en(zx_i2s->reg_base, true);
+ else
+ zx_i2s_tx_dma_en(zx_i2s->reg_base, true);
+ /* fall thru */
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (capture)
+ zx_i2s_rx_en(zx_i2s->reg_base, true);
+ else
+ zx_i2s_tx_en(zx_i2s->reg_base, true);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (capture)
+ zx_i2s_rx_dma_en(zx_i2s->reg_base, false);
+ else
+ zx_i2s_tx_dma_en(zx_i2s->reg_base, false);
+ /* fall thru */
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (capture)
+ zx_i2s_rx_en(zx_i2s->reg_base, false);
+ else
+ zx_i2s_tx_en(zx_i2s->reg_base, false);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int zx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+
+ return clk_prepare_enable(zx_i2s->dai_clk);
+}
+
+static void zx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+
+ clk_disable_unprepare(zx_i2s->dai_clk);
+}
+
+static struct snd_soc_dai_ops zx_i2s_dai_ops = {
+ .trigger = zx_i2s_trigger,
+ .hw_params = zx_i2s_hw_params,
+ .set_fmt = zx_i2s_set_fmt,
+ .startup = zx_i2s_startup,
+ .shutdown = zx_i2s_shutdown,
+};
+
+static const struct snd_soc_component_driver zx_i2s_component = {
+ .name = "zx-i2s",
+};
+
+static struct snd_soc_dai_driver zx_i2s_dai = {
+ .name = "zx-i2s-dai",
+ .id = 0,
+ .probe = zx_i2s_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = ZX_I2S_RATES,
+ .formats = ZX_I2S_FMTBIT,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ZX_I2S_RATES,
+ .formats = ZX_I2S_FMTBIT,
+ },
+ .ops = &zx_i2s_dai_ops,
+};
+
+static int zx_i2s_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct zx_i2s_info *zx_i2s;
+ int ret;
+
+ zx_i2s = kzalloc(sizeof(*zx_i2s), GFP_KERNEL);
+ if (!zx_i2s)
+ return -ENOMEM;
+
+ zx_i2s->dai_clk = devm_clk_get(&pdev->dev, "tx");
+ if (IS_ERR(zx_i2s->dai_clk)) {
+ dev_err(&pdev->dev, "Fail to get clk\n");
+ return PTR_ERR(zx_i2s->dai_clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ zx_i2s->mapbase = res->start;
+ zx_i2s->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (!zx_i2s->reg_base) {
+ dev_err(&pdev->dev, "ioremap failed!\n");
+ return -EIO;
+ }
+
+ 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);
+ if (ret) {
+ dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id zx_i2s_dt_ids[] = {
+ { .compatible = "zte,zx296702-i2s", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, zx_i2s_dt_ids);
+
+static struct platform_driver i2s_driver = {
+ .probe = zx_i2s_probe,
+ .driver = {
+ .name = "zx-i2s",
+ .of_match_table = zx_i2s_dt_ids,
+ },
+};
+
+module_platform_driver(i2s_driver);
+
+MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>");
+MODULE_DESCRIPTION("ZTE I2S SoC DAI");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/zte/zx296702-spdif.c b/sound/soc/zte/zx296702-spdif.c
new file mode 100644
index 000000000000..11a0e46a1156
--- /dev/null
+++ b/sound/soc/zte/zx296702-spdif.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2015 Linaro
+ *
+ * Author: Jun Nie <jun.nie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define ZX_CTRL 0x04
+#define ZX_FIFOCTRL 0x08
+#define ZX_INT_STATUS 0x10
+#define ZX_INT_MASK 0x14
+#define ZX_DATA 0x18
+#define ZX_VALID_BIT 0x1c
+#define ZX_CH_STA_1 0x20
+#define ZX_CH_STA_2 0x24
+#define ZX_CH_STA_3 0x28
+#define ZX_CH_STA_4 0x2c
+#define ZX_CH_STA_5 0x30
+#define ZX_CH_STA_6 0x34
+
+#define ZX_CTRL_MODA_16 (0 << 6)
+#define ZX_CTRL_MODA_18 BIT(6)
+#define ZX_CTRL_MODA_20 (2 << 6)
+#define ZX_CTRL_MODA_24 (3 << 6)
+#define ZX_CTRL_MODA_MASK (3 << 6)
+
+#define ZX_CTRL_ENB BIT(4)
+#define ZX_CTRL_DNB (0 << 4)
+#define ZX_CTRL_ENB_MASK BIT(4)
+
+#define ZX_CTRL_TX_OPEN BIT(0)
+#define ZX_CTRL_TX_CLOSE (0 << 0)
+#define ZX_CTRL_TX_MASK BIT(0)
+
+#define ZX_CTRL_OPEN (ZX_CTRL_TX_OPEN | ZX_CTRL_ENB)
+#define ZX_CTRL_CLOSE (ZX_CTRL_TX_CLOSE | ZX_CTRL_DNB)
+
+#define ZX_CTRL_DOUBLE_TRACK (0 << 8)
+#define ZX_CTRL_LEFT_TRACK BIT(8)
+#define ZX_CTRL_RIGHT_TRACK (2 << 8)
+#define ZX_CTRL_TRACK_MASK (3 << 8)
+
+#define ZX_FIFOCTRL_TXTH_MASK (0x1f << 8)
+#define ZX_FIFOCTRL_TXTH(x) (x << 8)
+#define ZX_FIFOCTRL_TX_DMA_EN BIT(2)
+#define ZX_FIFOCTRL_TX_DMA_DIS (0 << 2)
+#define ZX_FIFOCTRL_TX_DMA_EN_MASK BIT(2)
+#define ZX_FIFOCTRL_TX_FIFO_RST BIT(0)
+#define ZX_FIFOCTRL_TX_FIFO_RST_MASK BIT(0)
+
+#define ZX_VALID_DOUBLE_TRACK (0 << 0)
+#define ZX_VALID_LEFT_TRACK BIT(1)
+#define ZX_VALID_RIGHT_TRACK (2 << 0)
+#define ZX_VALID_TRACK_MASK (3 << 0)
+
+#define ZX_SPDIF_CLK_RAT (4 * 32)
+
+struct zx_spdif_info {
+ struct snd_dmaengine_dai_dma_data dma_data;
+ struct clk *dai_clk;
+ void __iomem *reg_base;
+ resource_size_t mapbase;
+};
+
+static int zx_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+
+ snd_soc_dai_set_drvdata(dai, zx_spdif);
+ zx_spdif->dma_data.addr = zx_spdif->mapbase + ZX_DATA;
+ zx_spdif->dma_data.maxburst = 8;
+ snd_soc_dai_init_dma_data(dai, &zx_spdif->dma_data, NULL);
+ return 0;
+}
+
+static int zx_spdif_chanstats(void __iomem *base, unsigned int rate)
+{
+ u32 cstas1;
+
+ switch (rate) {
+ case 22050:
+ cstas1 = IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ cstas1 = IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ cstas1 = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ cstas1 = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ cstas1 = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ cstas1 = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ cstas1 = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ cstas1 = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ cstas1 = IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cstas1 = cstas1 << 24;
+ cstas1 |= IEC958_AES0_CON_NOT_COPYRIGHT;
+
+ writel_relaxed(cstas1, base + ZX_CH_STA_1);
+ return 0;
+}
+
+static int zx_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *socdai)
+{
+ struct zx_spdif_info *zx_spdif = dev_get_drvdata(socdai->dev);
+ struct zx_spdif_info *spdif = snd_soc_dai_get_drvdata(socdai);
+ struct snd_dmaengine_dai_dma_data *dma_data = &zx_spdif->dma_data;
+ u32 val, ch_num, rate;
+ int ret;
+
+ dma_data = snd_soc_dai_get_dma_data(socdai, substream);
+ dma_data->addr_width = params_width(params) >> 3;
+
+ val = readl_relaxed(zx_spdif->reg_base + ZX_CTRL);
+ val &= ~ZX_CTRL_MODA_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= ZX_CTRL_MODA_16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ val |= ZX_CTRL_MODA_18;
+ break;
+
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= ZX_CTRL_MODA_20;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= ZX_CTRL_MODA_24;
+ break;
+ default:
+ dev_err(socdai->dev, "Format not support!\n");
+ return -EINVAL;
+ }
+
+ ch_num = params_channels(params);
+ if (ch_num == 2)
+ val |= ZX_CTRL_DOUBLE_TRACK;
+ else
+ val |= ZX_CTRL_LEFT_TRACK;
+ writel_relaxed(val, zx_spdif->reg_base + ZX_CTRL);
+
+ val = readl_relaxed(zx_spdif->reg_base + ZX_VALID_BIT);
+ val &= ~ZX_VALID_TRACK_MASK;
+ if (ch_num == 2)
+ val |= ZX_VALID_DOUBLE_TRACK;
+ else
+ val |= ZX_VALID_RIGHT_TRACK;
+ writel_relaxed(val, zx_spdif->reg_base + ZX_VALID_BIT);
+
+ rate = params_rate(params);
+ ret = zx_spdif_chanstats(zx_spdif->reg_base, rate);
+ if (ret)
+ return ret;
+ return clk_set_rate(spdif->dai_clk, rate * ch_num * ZX_SPDIF_CLK_RAT);
+}
+
+static void zx_spdif_cfg_tx(void __iomem *base, int on)
+{
+ u32 val;
+
+ val = readl_relaxed(base + ZX_CTRL);
+ val &= ~(ZX_CTRL_ENB_MASK | ZX_CTRL_TX_MASK);
+ val |= on ? ZX_CTRL_OPEN : ZX_CTRL_CLOSE;
+ writel_relaxed(val, base + ZX_CTRL);
+
+ val = readl_relaxed(base + ZX_FIFOCTRL);
+ val &= ~ZX_FIFOCTRL_TX_DMA_EN_MASK;
+ if (on)
+ val |= ZX_FIFOCTRL_TX_DMA_EN;
+ writel_relaxed(val, base + ZX_FIFOCTRL);
+}
+
+static int zx_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ u32 val;
+ struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ val = readl_relaxed(zx_spdif->reg_base + ZX_FIFOCTRL);
+ val |= ZX_FIFOCTRL_TX_FIFO_RST;
+ writel_relaxed(val, zx_spdif->reg_base + ZX_FIFOCTRL);
+ /* fall thru */
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ zx_spdif_cfg_tx(zx_spdif->reg_base, true);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ zx_spdif_cfg_tx(zx_spdif->reg_base, false);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int zx_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+
+ return clk_prepare_enable(zx_spdif->dai_clk);
+}
+
+static void zx_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+
+ clk_disable_unprepare(zx_spdif->dai_clk);
+}
+
+#define ZX_RATES \
+ (SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define ZX_FORMAT \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE \
+ | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops zx_spdif_dai_ops = {
+ .trigger = zx_spdif_trigger,
+ .startup = zx_spdif_startup,
+ .shutdown = zx_spdif_shutdown,
+ .hw_params = zx_spdif_hw_params,
+};
+
+static struct snd_soc_dai_driver zx_spdif_dai = {
+ .name = "spdif",
+ .id = 0,
+ .probe = zx_spdif_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ZX_RATES,
+ .formats = ZX_FORMAT,
+ },
+ .ops = &zx_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver zx_spdif_component = {
+ .name = "spdif",
+};
+
+static void zx_spdif_dev_init(void __iomem *base)
+{
+ u32 val;
+
+ writel_relaxed(0, base + ZX_CTRL);
+ writel_relaxed(0, base + ZX_INT_MASK);
+ writel_relaxed(0xf, base + ZX_INT_STATUS);
+ writel_relaxed(0x1, base + ZX_FIFOCTRL);
+
+ val = readl_relaxed(base + ZX_FIFOCTRL);
+ val &= ~(ZX_FIFOCTRL_TXTH_MASK | ZX_FIFOCTRL_TX_FIFO_RST_MASK);
+ val |= ZX_FIFOCTRL_TXTH(8);
+ writel_relaxed(val, base + ZX_FIFOCTRL);
+}
+
+static int zx_spdif_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct zx_spdif_info *zx_spdif;
+ int ret;
+
+ zx_spdif = devm_kzalloc(&pdev->dev, sizeof(*zx_spdif), GFP_KERNEL);
+ if (!zx_spdif)
+ return -ENOMEM;
+
+ zx_spdif->dai_clk = devm_clk_get(&pdev->dev, "tx");
+ if (IS_ERR(zx_spdif->dai_clk)) {
+ dev_err(&pdev->dev, "Fail to get clk\n");
+ return PTR_ERR(zx_spdif->dai_clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ zx_spdif->mapbase = res->start;
+ zx_spdif->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (!zx_spdif->reg_base) {
+ dev_err(&pdev->dev, "ioremap failed!\n");
+ return -EIO;
+ }
+
+ zx_spdif_dev_init(zx_spdif->reg_base);
+ platform_set_drvdata(pdev, zx_spdif);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &zx_spdif_component,
+ &zx_spdif_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id zx_spdif_dt_ids[] = {
+ { .compatible = "zte,zx296702-spdif", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, zx_spdif_dt_ids);
+
+static struct platform_driver spdif_driver = {
+ .probe = zx_spdif_probe,
+ .driver = {
+ .name = "zx-spdif",
+ .of_match_table = zx_spdif_dt_ids,
+ },
+};
+
+module_platform_driver(spdif_driver);
+
+MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>");
+MODULE_DESCRIPTION("ZTE SPDIF SoC DAI");
+MODULE_LICENSE("GPL");
diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c
index b155137ee312..026347643c81 100644
--- a/sound/sound_firmware.c
+++ b/sound/sound_firmware.c
@@ -12,7 +12,6 @@ static int do_mod_firmware_load(const char *fn, char **fp)
struct file* filp;
long l;
char *dp;
- loff_t pos;
filp = filp_open(fn, 0, 0);
if (IS_ERR(filp))
@@ -34,8 +33,7 @@ static int do_mod_firmware_load(const char *fn, char **fp)
fput(filp);
return 0;
}
- pos = 0;
- if (vfs_read(filp, dp, l, &pos) != l)
+ if (kernel_read(filp, 0, dp, l) != l)
{
printk(KERN_INFO "Failed to read '%s'.\n", fn);
vfree(dp);
diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile
index 328594e6152d..fb761c2c2b50 100644
--- a/sound/synth/emux/Makefile
+++ b/sound/synth/emux/Makefile
@@ -4,8 +4,9 @@
#
snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \
- emux_effect.o emux_proc.o emux_hwdep.o soundfont.o \
- $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o)
+ emux_effect.o emux_hwdep.o soundfont.o
+snd-emux-synth-$(CONFIG_SND_PROC_FS) += emux_proc.o
+snd-emux-synth-$(CONFIG_SND_SEQUENCER_OSS) += emux_oss.o
# Toplevel Module Dependencies
obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emux-synth.o
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index 49195325fdf6..9312cd8a6fdd 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -128,9 +128,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
#endif
snd_emux_init_virmidi(emu, card);
-#ifdef CONFIG_PROC_FS
snd_emux_proc_init(emu, card, index);
-#endif
return 0;
}
@@ -150,9 +148,7 @@ int snd_emux_free(struct snd_emux *emu)
del_timer(&emu->tlist);
spin_unlock_irqrestore(&emu->voice_lock, flags);
-#ifdef CONFIG_PROC_FS
snd_emux_proc_free(emu);
-#endif
snd_emux_delete_virmidi(emu);
#ifdef CONFIG_SND_SEQUENCER_OSS
snd_emux_detach_seq_oss(emu);
diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
index 58a32a10d115..a82b4053bee8 100644
--- a/sound/synth/emux/emux_proc.c
+++ b/sound/synth/emux/emux_proc.c
@@ -24,8 +24,6 @@
#include <sound/info.h>
#include "emux_voice.h"
-#ifdef CONFIG_PROC_FS
-
static void
snd_emux_proc_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buf)
@@ -128,5 +126,3 @@ void snd_emux_proc_free(struct snd_emux *emu)
snd_info_free_entry(emu->proc);
emu->proc = NULL;
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h
index 09711f84ed30..a7073c371bcc 100644
--- a/sound/synth/emux/emux_voice.h
+++ b/sound/synth/emux/emux_voice.h
@@ -82,9 +82,13 @@ void snd_emux_init_seq_oss(struct snd_emux *emu);
void snd_emux_detach_seq_oss(struct snd_emux *emu);
/* emux_proc.c */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device);
void snd_emux_proc_free(struct snd_emux *emu);
+#else
+static inline void snd_emux_proc_init(struct snd_emux *emu,
+ struct snd_card *card, int device) {}
+static inline void snd_emux_proc_free(struct snd_emux *emu) {}
#endif
#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON)
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index 820d6ca8c458..d060dddcc52d 100644
--- a/sound/usb/bcd2000/bcd2000.c
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -70,7 +70,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static DEFINE_MUTEX(devices_mutex);
-DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
static struct usb_driver bcd2000_driver;
#ifdef CONFIG_SND_DEBUG
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 8b7e391dd0b8..6b3acba5da7a 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -809,12 +809,12 @@ static struct usb_feature_control_info audio_feature_info[] = {
{ "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 },
+ { "Delay Control", USB_MIXER_U16 }, /* FIXME: U32 in UAC2 */
{ "Bass Boost", USB_MIXER_BOOLEAN },
{ "Loudness", USB_MIXER_BOOLEAN },
/* UAC2 specific */
- { "Input Gain Control", USB_MIXER_U16 },
- { "Input Gain Pad Control", USB_MIXER_BOOLEAN },
+ { "Input Gain Control", USB_MIXER_S16 },
+ { "Input Gain Pad Control", USB_MIXER_S16 },
{ "Phase Inverter Control", USB_MIXER_BOOLEAN },
};