summaryrefslogtreecommitdiffstats
path: root/kernel/sound
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/sound
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/sound')
-rw-r--r--kernel/sound/ac97_bus.c82
-rw-r--r--kernel/sound/aoa/codecs/onyx.c1
-rw-r--r--kernel/sound/aoa/codecs/tas.c1
-rw-r--r--kernel/sound/aoa/fabrics/layout.c21
-rw-r--r--kernel/sound/aoa/soundbus/core.c32
-rw-r--r--kernel/sound/aoa/soundbus/soundbus.h4
-rw-r--r--kernel/sound/aoa/soundbus/sysfs.c13
-rw-r--r--kernel/sound/arm/Kconfig15
-rw-r--r--kernel/sound/arm/pxa2xx-ac97.c13
-rw-r--r--kernel/sound/arm/pxa2xx-pcm-lib.c201
-rw-r--r--kernel/sound/arm/pxa2xx-pcm.c12
-rw-r--r--kernel/sound/arm/pxa2xx-pcm.h2
-rw-r--r--kernel/sound/core/Kconfig33
-rw-r--r--kernel/sound/core/Makefile16
-rw-r--r--kernel/sound/core/compress_offload.c11
-rw-r--r--kernel/sound/core/control.c2
-rw-r--r--kernel/sound/core/control_compat.c90
-rw-r--r--kernel/sound/core/ctljack.c44
-rw-r--r--kernel/sound/core/hrtimer.c12
-rw-r--r--kernel/sound/core/hwdep.c6
-rw-r--r--kernel/sound/core/info.c833
-rw-r--r--kernel/sound/core/info_oss.c29
-rw-r--r--kernel/sound/core/init.c62
-rw-r--r--kernel/sound/core/jack.c146
-rw-r--r--kernel/sound/core/memalloc.c2
-rw-r--r--kernel/sound/core/oss/mixer_oss.c9
-rw-r--r--kernel/sound/core/oss/pcm_oss.c21
-rw-r--r--kernel/sound/core/pcm.c9
-rw-r--r--kernel/sound/core/pcm_compat.c190
-rw-r--r--kernel/sound/core/pcm_drm_eld.c99
-rw-r--r--kernel/sound/core/pcm_iec958.c95
-rw-r--r--kernel/sound/core/pcm_lib.c24
-rw-r--r--kernel/sound/core/pcm_native.c58
-rw-r--r--kernel/sound/core/rawmidi.c134
-rw-r--r--kernel/sound/core/rawmidi_compat.c53
-rw-r--r--kernel/sound/core/seq/Makefile3
-rw-r--r--kernel/sound/core/seq/oss/seq_oss.c8
-rw-r--r--kernel/sound/core/seq/oss/seq_oss_device.h1
-rw-r--r--kernel/sound/core/seq/oss/seq_oss_init.c23
-rw-r--r--kernel/sound/core/seq/oss/seq_oss_midi.c4
-rw-r--r--kernel/sound/core/seq/oss/seq_oss_readq.c10
-rw-r--r--kernel/sound/core/seq/oss/seq_oss_synth.c6
-rw-r--r--kernel/sound/core/seq/oss/seq_oss_writeq.c4
-rw-r--r--kernel/sound/core/seq/seq_clientmgr.c9
-rw-r--r--kernel/sound/core/seq/seq_compat.c9
-rw-r--r--kernel/sound/core/seq/seq_device.c6
-rw-r--r--kernel/sound/core/seq/seq_info.c19
-rw-r--r--kernel/sound/core/seq/seq_info.h2
-rw-r--r--kernel/sound/core/seq/seq_memory.c13
-rw-r--r--kernel/sound/core/seq/seq_ports.c236
-rw-r--r--kernel/sound/core/seq/seq_queue.c6
-rw-r--r--kernel/sound/core/seq/seq_timer.c91
-rw-r--r--kernel/sound/core/seq/seq_virmidi.c23
-rw-r--r--kernel/sound/core/sound.c28
-rw-r--r--kernel/sound/core/sound_oss.c34
-rw-r--r--kernel/sound/core/timer.c194
-rw-r--r--kernel/sound/core/timer_compat.c18
-rw-r--r--kernel/sound/drivers/aloop.c8
-rw-r--r--kernel/sound/drivers/dummy.c53
-rw-r--r--kernel/sound/drivers/opl4/Makefile3
-rw-r--r--kernel/sound/drivers/opl4/opl4_lib.c4
-rw-r--r--kernel/sound/drivers/opl4/opl4_local.h7
-rw-r--r--kernel/sound/drivers/opl4/opl4_proc.c4
-rw-r--r--kernel/sound/drivers/pcsp/pcsp.c17
-rw-r--r--kernel/sound/firewire/Kconfig30
-rw-r--r--kernel/sound/firewire/Makefile5
-rw-r--r--kernel/sound/firewire/amdtp-am824.c465
-rw-r--r--kernel/sound/firewire/amdtp-am824.h52
-rw-r--r--kernel/sound/firewire/amdtp-stream.c (renamed from kernel/sound/firewire/amdtp.c)570
-rw-r--r--kernel/sound/firewire/amdtp-stream.h (renamed from kernel/sound/firewire/amdtp.h)120
-rw-r--r--kernel/sound/firewire/bebob/Makefile2
-rw-r--r--kernel/sound/firewire/bebob/bebob.c26
-rw-r--r--kernel/sound/firewire/bebob/bebob.h54
-rw-r--r--kernel/sound/firewire/bebob/bebob_focusrite.c59
-rw-r--r--kernel/sound/firewire/bebob/bebob_maudio.c57
-rw-r--r--kernel/sound/firewire/bebob/bebob_midi.c24
-rw-r--r--kernel/sound/firewire/bebob/bebob_pcm.c50
-rw-r--r--kernel/sound/firewire/bebob/bebob_proc.c28
-rw-r--r--kernel/sound/firewire/bebob/bebob_stream.c204
-rw-r--r--kernel/sound/firewire/bebob/bebob_terratec.c40
-rw-r--r--kernel/sound/firewire/bebob/bebob_yamaha.c26
-rw-r--r--kernel/sound/firewire/dice/Makefile2
-rw-r--r--kernel/sound/firewire/dice/dice-midi.c12
-rw-r--r--kernel/sound/firewire/dice/dice-pcm.c30
-rw-r--r--kernel/sound/firewire/dice/dice-stream.c34
-rw-r--r--kernel/sound/firewire/dice/dice.c7
-rw-r--r--kernel/sound/firewire/dice/dice.h2
-rw-r--r--kernel/sound/firewire/digi00x/Makefile4
-rw-r--r--kernel/sound/firewire/digi00x/amdtp-dot.c442
-rw-r--r--kernel/sound/firewire/digi00x/digi00x-hwdep.c200
-rw-r--r--kernel/sound/firewire/digi00x/digi00x-midi.c223
-rw-r--r--kernel/sound/firewire/digi00x/digi00x-pcm.c373
-rw-r--r--kernel/sound/firewire/digi00x/digi00x-proc.c99
-rw-r--r--kernel/sound/firewire/digi00x/digi00x-stream.c422
-rw-r--r--kernel/sound/firewire/digi00x/digi00x-transaction.c137
-rw-r--r--kernel/sound/firewire/digi00x/digi00x.c170
-rw-r--r--kernel/sound/firewire/digi00x/digi00x.h157
-rw-r--r--kernel/sound/firewire/fcp.c2
-rw-r--r--kernel/sound/firewire/fireworks/Makefile2
-rw-r--r--kernel/sound/firewire/fireworks/fireworks.c12
-rw-r--r--kernel/sound/firewire/fireworks/fireworks.h2
-rw-r--r--kernel/sound/firewire/fireworks/fireworks_command.c2
-rw-r--r--kernel/sound/firewire/fireworks/fireworks_midi.c12
-rw-r--r--kernel/sound/firewire/fireworks/fireworks_pcm.c30
-rw-r--r--kernel/sound/firewire/fireworks/fireworks_stream.c8
-rw-r--r--kernel/sound/firewire/lib.c142
-rw-r--r--kernel/sound/firewire/lib.h56
-rw-r--r--kernel/sound/firewire/oxfw/Makefile2
-rw-r--r--kernel/sound/firewire/oxfw/oxfw-midi.c40
-rw-r--r--kernel/sound/firewire/oxfw/oxfw-pcm.c27
-rw-r--r--kernel/sound/firewire/oxfw/oxfw-stream.c72
-rw-r--r--kernel/sound/firewire/oxfw/oxfw.c46
-rw-r--r--kernel/sound/firewire/oxfw/oxfw.h3
-rw-r--r--kernel/sound/firewire/tascam/Makefile4
-rw-r--r--kernel/sound/firewire/tascam/amdtp-tascam.c243
-rw-r--r--kernel/sound/firewire/tascam/tascam-hwdep.c201
-rw-r--r--kernel/sound/firewire/tascam/tascam-midi.c135
-rw-r--r--kernel/sound/firewire/tascam/tascam-pcm.c312
-rw-r--r--kernel/sound/firewire/tascam/tascam-proc.c88
-rw-r--r--kernel/sound/firewire/tascam/tascam-stream.c496
-rw-r--r--kernel/sound/firewire/tascam/tascam-transaction.c302
-rw-r--r--kernel/sound/firewire/tascam/tascam.c209
-rw-r--r--kernel/sound/firewire/tascam/tascam.h147
-rw-r--r--kernel/sound/hda/Kconfig26
-rw-r--r--kernel/sound/hda/Makefile8
-rw-r--r--kernel/sound/hda/ext/Makefile3
-rw-r--r--kernel/sound/hda/ext/hdac_ext_bus.c265
-rw-r--r--kernel/sound/hda/ext/hdac_ext_controller.c302
-rw-r--r--kernel/sound/hda/ext/hdac_ext_stream.c499
-rw-r--r--kernel/sound/hda/hda_bus_type.c54
-rw-r--r--kernel/sound/hda/hdac_bus.c34
-rw-r--r--kernel/sound/hda/hdac_controller.c507
-rw-r--r--kernel/sound/hda/hdac_device.c491
-rw-r--r--kernel/sound/hda/hdac_i915.c279
-rw-r--r--kernel/sound/hda/hdac_regmap.c20
-rw-r--r--kernel/sound/hda/hdac_stream.c720
-rw-r--r--kernel/sound/hda/hdac_sysfs.c14
-rw-r--r--kernel/sound/hda/trace.h27
-rw-r--r--kernel/sound/i2c/other/ak4xxx-adda.c4
-rw-r--r--kernel/sound/isa/Kconfig4
-rw-r--r--kernel/sound/isa/gus/gus_mixer.c9
-rw-r--r--kernel/sound/oss/ad1848.c2
-rw-r--r--kernel/sound/oss/msnd_pinnacle.c3
-rw-r--r--kernel/sound/oss/sb_audio.c8
-rw-r--r--kernel/sound/pci/Kconfig27
-rw-r--r--kernel/sound/pci/ac97/Makefile2
-rw-r--r--kernel/sound/pci/ac97/ac97_local.h2
-rw-r--r--kernel/sound/pci/ad1889.c4
-rw-r--r--kernel/sound/pci/ak4531_codec.c6
-rw-r--r--kernel/sound/pci/ali5451/ali5451.c4
-rw-r--r--kernel/sound/pci/als300.c4
-rw-r--r--kernel/sound/pci/als4000.c4
-rw-r--r--kernel/sound/pci/asihpi/hpioctl.c1
-rw-r--r--kernel/sound/pci/atiixp.c4
-rw-r--r--kernel/sound/pci/atiixp_modem.c4
-rw-r--r--kernel/sound/pci/au88x0/au88x0.c4
-rw-r--r--kernel/sound/pci/aw2/aw2-alsa.c4
-rw-r--r--kernel/sound/pci/azt3328.c4
-rw-r--r--kernel/sound/pci/ca0106/Makefile3
-rw-r--r--kernel/sound/pci/ca0106/ca0106_main.c6
-rw-r--r--kernel/sound/pci/ca0106/ca0106_proc.c4
-rw-r--r--kernel/sound/pci/cmipci.c5
-rw-r--r--kernel/sound/pci/cs46xx/cs46xx_lib.c13
-rw-r--r--kernel/sound/pci/cs46xx/cs46xx_lib.h4
-rw-r--r--kernel/sound/pci/cs46xx/dsp_spos.c4
-rw-r--r--kernel/sound/pci/cs46xx/dsp_spos_scb_lib.c6
-rw-r--r--kernel/sound/pci/cs5535audio/cs5535audio.c4
-rw-r--r--kernel/sound/pci/ctxfi/ctamixer.c6
-rw-r--r--kernel/sound/pci/ctxfi/ctamixer.h2
-rw-r--r--kernel/sound/pci/ctxfi/ctdaio.c10
-rw-r--r--kernel/sound/pci/ctxfi/ctdaio.h4
-rw-r--r--kernel/sound/pci/ctxfi/cthw20k1.c4
-rw-r--r--kernel/sound/pci/ctxfi/cthw20k2.c4
-rw-r--r--kernel/sound/pci/ctxfi/ctresource.c2
-rw-r--r--kernel/sound/pci/ctxfi/ctresource.h2
-rw-r--r--kernel/sound/pci/ctxfi/ctsrc.c6
-rw-r--r--kernel/sound/pci/ctxfi/ctsrc.h4
-rw-r--r--kernel/sound/pci/echoaudio/darla20_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/darla24_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/echo3g_dsp.c16
-rw-r--r--kernel/sound/pci/echoaudio/echoaudio.c2
-rw-r--r--kernel/sound/pci/echoaudio/echoaudio.h7
-rw-r--r--kernel/sound/pci/echoaudio/echoaudio_3g.c12
-rw-r--r--kernel/sound/pci/echoaudio/echoaudio_dsp.c26
-rw-r--r--kernel/sound/pci/echoaudio/echoaudio_gml.c4
-rw-r--r--kernel/sound/pci/echoaudio/gina20.c2
-rw-r--r--kernel/sound/pci/echoaudio/gina20_dsp.c8
-rw-r--r--kernel/sound/pci/echoaudio/gina24_dsp.c22
-rw-r--r--kernel/sound/pci/echoaudio/indigo_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/indigodj_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/indigodjx_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/indigoio_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/indigoiox_dsp.c6
-rw-r--r--kernel/sound/pci/echoaudio/layla20.c2
-rw-r--r--kernel/sound/pci/echoaudio/layla20_dsp.c12
-rw-r--r--kernel/sound/pci/echoaudio/layla24_dsp.c26
-rw-r--r--kernel/sound/pci/echoaudio/mia.c2
-rw-r--r--kernel/sound/pci/echoaudio/mia_dsp.c8
-rw-r--r--kernel/sound/pci/echoaudio/mona_dsp.c20
-rw-r--r--kernel/sound/pci/emu10k1/Makefile3
-rw-r--r--kernel/sound/pci/emu10k1/emu10k1_main.c6
-rw-r--r--kernel/sound/pci/emu10k1/emumixer.c25
-rw-r--r--kernel/sound/pci/emu10k1/emuproc.c2
-rw-r--r--kernel/sound/pci/es1938.c4
-rw-r--r--kernel/sound/pci/es1968.c4
-rw-r--r--kernel/sound/pci/hda/Kconfig31
-rw-r--r--kernel/sound/pci/hda/Makefile9
-rw-r--r--kernel/sound/pci/hda/hda_auto_parser.c27
-rw-r--r--kernel/sound/pci/hda/hda_beep.c2
-rw-r--r--kernel/sound/pci/hda/hda_beep.h2
-rw-r--r--kernel/sound/pci/hda/hda_bind.c111
-rw-r--r--kernel/sound/pci/hda/hda_codec.c550
-rw-r--r--kernel/sound/pci/hda/hda_codec.h130
-rw-r--r--kernel/sound/pci/hda/hda_controller.c1351
-rw-r--r--kernel/sound/pci/hda/hda_controller.h274
-rw-r--r--kernel/sound/pci/hda/hda_controller_trace.h98
-rw-r--r--kernel/sound/pci/hda/hda_eld.c14
-rw-r--r--kernel/sound/pci/hda/hda_generic.c100
-rw-r--r--kernel/sound/pci/hda/hda_i915.c196
-rw-r--r--kernel/sound/pci/hda/hda_intel.c496
-rw-r--r--kernel/sound/pci/hda/hda_intel.h28
-rw-r--r--kernel/sound/pci/hda/hda_intel_trace.h55
-rw-r--r--kernel/sound/pci/hda/hda_jack.c104
-rw-r--r--kernel/sound/pci/hda/hda_jack.h7
-rw-r--r--kernel/sound/pci/hda/hda_local.h11
-rw-r--r--kernel/sound/pci/hda/hda_proc.c107
-rw-r--r--kernel/sound/pci/hda/hda_sysfs.c3
-rw-r--r--kernel/sound/pci/hda/hda_tegra.c132
-rw-r--r--kernel/sound/pci/hda/patch_analog.c40
-rw-r--r--kernel/sound/pci/hda/patch_ca0110.c18
-rw-r--r--kernel/sound/pci/hda/patch_ca0132.c225
-rw-r--r--kernel/sound/pci/hda/patch_cirrus.c62
-rw-r--r--kernel/sound/pci/hda/patch_cmedia.c19
-rw-r--r--kernel/sound/pci/hda/patch_conexant.c123
-rw-r--r--kernel/sound/pci/hda/patch_hdmi.c447
-rw-r--r--kernel/sound/pci/hda/patch_realtek.c879
-rw-r--r--kernel/sound/pci/hda/patch_si3054.c39
-rw-r--r--kernel/sound/pci/hda/patch_sigmatel.c294
-rw-r--r--kernel/sound/pci/hda/patch_via.c192
-rw-r--r--kernel/sound/pci/ice1712/ice1712.c4
-rw-r--r--kernel/sound/pci/ice1712/quartet.c7
-rw-r--r--kernel/sound/pci/intel8x0.c4
-rw-r--r--kernel/sound/pci/intel8x0m.c5
-rw-r--r--kernel/sound/pci/korg1212/korg1212.c8
-rw-r--r--kernel/sound/pci/lx6464es/lx6464es.c22
-rw-r--r--kernel/sound/pci/maestro3.c29
-rw-r--r--kernel/sound/pci/mixart/mixart.c2
-rw-r--r--kernel/sound/pci/oxygen/oxygen_lib.c4
-rw-r--r--kernel/sound/pci/oxygen/oxygen_mixer.c2
-rw-r--r--kernel/sound/pci/oxygen/xonar_wm87x6.c2
-rw-r--r--kernel/sound/pci/pcxhr/pcxhr.c2
-rw-r--r--kernel/sound/pci/rme32.c4
-rw-r--r--kernel/sound/pci/rme96.c49
-rw-r--r--kernel/sound/pci/rme9652/hdsp.c12
-rw-r--r--kernel/sound/pci/rme9652/hdspm.c25
-rw-r--r--kernel/sound/pci/sis7019.c10
-rw-r--r--kernel/sound/pci/sonicvibes.c4
-rw-r--r--kernel/sound/pci/trident/trident_main.c4
-rw-r--r--kernel/sound/ppc/keywest.c38
-rw-r--r--kernel/sound/soc/Kconfig12
-rw-r--r--kernel/sound/soc/Makefile11
-rw-r--r--kernel/sound/soc/atmel/Kconfig46
-rw-r--r--kernel/sound/soc/atmel/Makefile2
-rw-r--r--kernel/sound/soc/atmel/atmel-classd.c679
-rw-r--r--kernel/sound/soc/atmel/atmel-classd.h120
-rw-r--r--kernel/sound/soc/atmel/atmel-pcm-dma.c3
-rw-r--r--kernel/sound/soc/atmel/atmel_ssc_dai.c2
-rw-r--r--kernel/sound/soc/atmel/atmel_wm8904.c1
-rw-r--r--kernel/sound/soc/atmel/sam9g20_wm8731.c10
-rw-r--r--kernel/sound/soc/au1x/db1000.c10
-rw-r--r--kernel/sound/soc/au1x/db1200.c16
-rw-r--r--kernel/sound/soc/au1x/dbdma2.c11
-rw-r--r--kernel/sound/soc/au1x/dma.c11
-rw-r--r--kernel/sound/soc/au1x/psc-i2s.c17
-rw-r--r--kernel/sound/soc/bcm/bcm2835-i2s.c2
-rw-r--r--kernel/sound/soc/blackfin/bf5xx-ac97-pcm.c10
-rw-r--r--kernel/sound/soc/blackfin/bf5xx-ad1836.c11
-rw-r--r--kernel/sound/soc/blackfin/bf5xx-i2s-pcm.c10
-rw-r--r--kernel/sound/soc/blackfin/bfin-eval-adau1373.c12
-rw-r--r--kernel/sound/soc/blackfin/bfin-eval-adau1701.c12
-rw-r--r--kernel/sound/soc/blackfin/bfin-eval-adau1x61.c1
-rw-r--r--kernel/sound/soc/blackfin/bfin-eval-adav80x.c12
-rw-r--r--kernel/sound/soc/cirrus/ep93xx-pcm.c1
-rw-r--r--kernel/sound/soc/codecs/88pm860x-codec.c51
-rw-r--r--kernel/sound/soc/codecs/Kconfig65
-rw-r--r--kernel/sound/soc/codecs/Makefile24
-rw-r--r--kernel/sound/soc/codecs/ab8500-codec.c27
-rw-r--r--kernel/sound/soc/codecs/ac97.c8
-rw-r--r--kernel/sound/soc/codecs/ad1836.c3
-rw-r--r--kernel/sound/soc/codecs/ad193x-i2c.c8
-rw-r--r--kernel/sound/soc/codecs/ad193x-spi.c17
-rw-r--r--kernel/sound/soc/codecs/ad193x.c128
-rw-r--r--kernel/sound/soc/codecs/ad193x.h9
-rw-r--r--kernel/sound/soc/codecs/ad1980.c36
-rw-r--r--kernel/sound/soc/codecs/adau1373.c23
-rw-r--r--kernel/sound/soc/codecs/adau1701.c127
-rw-r--r--kernel/sound/soc/codecs/adau1761-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/adau1761-spi.c1
-rw-r--r--kernel/sound/soc/codecs/adau1761.c27
-rw-r--r--kernel/sound/soc/codecs/adau1781-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/adau1781-spi.c1
-rw-r--r--kernel/sound/soc/codecs/adau1781.c10
-rw-r--r--kernel/sound/soc/codecs/adau17x1.c20
-rw-r--r--kernel/sound/soc/codecs/adau1977-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/adau1977-spi.c1
-rw-r--r--kernel/sound/soc/codecs/adau1977.c14
-rw-r--r--kernel/sound/soc/codecs/adav801.c1
-rw-r--r--kernel/sound/soc/codecs/adav803.c1
-rw-r--r--kernel/sound/soc/codecs/adav80x.c17
-rw-r--r--kernel/sound/soc/codecs/ak4104.c1
-rw-r--r--kernel/sound/soc/codecs/ak4535.c2
-rw-r--r--kernel/sound/soc/codecs/ak4613.c497
-rw-r--r--kernel/sound/soc/codecs/ak4641.c4
-rw-r--r--kernel/sound/soc/codecs/ak4642.c188
-rw-r--r--kernel/sound/soc/codecs/ak4671.c2
-rw-r--r--kernel/sound/soc/codecs/alc5623.c11
-rw-r--r--kernel/sound/soc/codecs/alc5632.c11
-rw-r--r--kernel/sound/soc/codecs/arizona.c348
-rw-r--r--kernel/sound/soc/codecs/arizona.h52
-rw-r--r--kernel/sound/soc/codecs/bt-sco.c11
-rw-r--r--kernel/sound/soc/codecs/cq93vc.c1
-rw-r--r--kernel/sound/soc/codecs/cs35l32.c61
-rw-r--r--kernel/sound/soc/codecs/cs35l32.h2
-rw-r--r--kernel/sound/soc/codecs/cs4265.c30
-rw-r--r--kernel/sound/soc/codecs/cs4270.c1
-rw-r--r--kernel/sound/soc/codecs/cs4271-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/cs4271-spi.c1
-rw-r--r--kernel/sound/soc/codecs/cs42l51-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/cs42l52.c70
-rw-r--r--kernel/sound/soc/codecs/cs42l56.c76
-rw-r--r--kernel/sound/soc/codecs/cs42l73.c118
-rw-r--r--kernel/sound/soc/codecs/cs42xx8-i2c.c4
-rw-r--r--kernel/sound/soc/codecs/cs42xx8.c21
-rw-r--r--kernel/sound/soc/codecs/cs42xx8.h1
-rw-r--r--kernel/sound/soc/codecs/cs4349.c392
-rw-r--r--kernel/sound/soc/codecs/cs4349.h136
-rw-r--r--kernel/sound/soc/codecs/cx20442.c6
-rw-r--r--kernel/sound/soc/codecs/da7210.c30
-rw-r--r--kernel/sound/soc/codecs/da7213.c211
-rw-r--r--kernel/sound/soc/codecs/da7213.h8
-rw-r--r--kernel/sound/soc/codecs/da7219-aad.c823
-rw-r--r--kernel/sound/soc/codecs/da7219-aad.h212
-rw-r--r--kernel/sound/soc/codecs/da7219.c1955
-rw-r--r--kernel/sound/soc/codecs/da7219.h820
-rw-r--r--kernel/sound/soc/codecs/da732x.c19
-rw-r--r--kernel/sound/soc/codecs/da9055.c22
-rw-r--r--kernel/sound/soc/codecs/es8328.c46
-rw-r--r--kernel/sound/soc/codecs/es8328.h1
-rw-r--r--kernel/sound/soc/codecs/gtm601.c95
-rw-r--r--kernel/sound/soc/codecs/hdmi.c109
-rw-r--r--kernel/sound/soc/codecs/ics43432.c76
-rw-r--r--kernel/sound/soc/codecs/isabelle.c13
-rw-r--r--kernel/sound/soc/codecs/jz4740.c11
-rw-r--r--kernel/sound/soc/codecs/lm4857.c115
-rw-r--r--kernel/sound/soc/codecs/lm49453.c23
-rw-r--r--kernel/sound/soc/codecs/max9768.c71
-rw-r--r--kernel/sound/soc/codecs/max98088.c332
-rw-r--r--kernel/sound/soc/codecs/max98088.h2
-rw-r--r--kernel/sound/soc/codecs/max98090.c201
-rw-r--r--kernel/sound/soc/codecs/max98090.h1
-rw-r--r--kernel/sound/soc/codecs/max98095.c370
-rw-r--r--kernel/sound/soc/codecs/max98357a.c26
-rw-r--r--kernel/sound/soc/codecs/max9850.c11
-rw-r--r--kernel/sound/soc/codecs/max9877.c33
-rw-r--r--kernel/sound/soc/codecs/max98925.c6
-rw-r--r--kernel/sound/soc/codecs/mc13783.c6
-rw-r--r--kernel/sound/soc/codecs/ml26124.c64
-rw-r--r--kernel/sound/soc/codecs/nau8825.c1340
-rw-r--r--kernel/sound/soc/codecs/nau8825.h341
-rw-r--r--kernel/sound/soc/codecs/pcm1681.c14
-rw-r--r--kernel/sound/soc/codecs/pcm1792a.c1
-rw-r--r--kernel/sound/soc/codecs/pcm512x-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/pcm512x-spi.c1
-rw-r--r--kernel/sound/soc/codecs/pcm512x.c12
-rw-r--r--kernel/sound/soc/codecs/rl6231.c104
-rw-r--r--kernel/sound/soc/codecs/rl6231.h1
-rw-r--r--kernel/sound/soc/codecs/rl6347a.c111
-rw-r--r--kernel/sound/soc/codecs/rl6347a.h34
-rw-r--r--kernel/sound/soc/codecs/rt286.c146
-rw-r--r--kernel/sound/soc/codecs/rt298.c1274
-rw-r--r--kernel/sound/soc/codecs/rt298.h206
-rw-r--r--kernel/sound/soc/codecs/rt5631.c13
-rw-r--r--kernel/sound/soc/codecs/rt5640.c60
-rw-r--r--kernel/sound/soc/codecs/rt5645.c1403
-rw-r--r--kernel/sound/soc/codecs/rt5645.h102
-rw-r--r--kernel/sound/soc/codecs/rt5651.c24
-rw-r--r--kernel/sound/soc/codecs/rt5670.c50
-rw-r--r--kernel/sound/soc/codecs/rt5670.h12
-rw-r--r--kernel/sound/soc/codecs/rt5677-spi.c234
-rw-r--r--kernel/sound/soc/codecs/rt5677-spi.h8
-rw-r--r--kernel/sound/soc/codecs/rt5677.c358
-rw-r--r--kernel/sound/soc/codecs/rt5677.h18
-rw-r--r--kernel/sound/soc/codecs/sgtl5000.c71
-rw-r--r--kernel/sound/soc/codecs/sgtl5000.h2
-rw-r--r--kernel/sound/soc/codecs/si476x.c2
-rw-r--r--kernel/sound/soc/codecs/sirf-audio-codec.c6
-rw-r--r--kernel/sound/soc/codecs/sn95031.c12
-rw-r--r--kernel/sound/soc/codecs/ssm2518.c25
-rw-r--r--kernel/sound/soc/codecs/ssm2602-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/ssm2602-spi.c1
-rw-r--r--kernel/sound/soc/codecs/ssm2602.c12
-rw-r--r--kernel/sound/soc/codecs/ssm4567.c43
-rw-r--r--kernel/sound/soc/codecs/sta32x.c20
-rw-r--r--kernel/sound/soc/codecs/sta350.c10
-rw-r--r--kernel/sound/soc/codecs/sta529.c12
-rw-r--r--kernel/sound/soc/codecs/stac9766.c60
-rw-r--r--kernel/sound/soc/codecs/sti-sas.c628
-rw-r--r--kernel/sound/soc/codecs/tas2552.c448
-rw-r--r--kernel/sound/soc/codecs/tas2552.h155
-rw-r--r--kernel/sound/soc/codecs/tas5086.c11
-rw-r--r--kernel/sound/soc/codecs/tas571x.c514
-rw-r--r--kernel/sound/soc/codecs/tas571x.h33
-rw-r--r--kernel/sound/soc/codecs/tfa9879.c3
-rw-r--r--kernel/sound/soc/codecs/tlv320aic23-spi.c1
-rw-r--r--kernel/sound/soc/codecs/tlv320aic23.c1
-rw-r--r--kernel/sound/soc/codecs/tlv320aic26.c1
-rw-r--r--kernel/sound/soc/codecs/tlv320aic31xx.c14
-rw-r--r--kernel/sound/soc/codecs/tlv320aic32x4.c2
-rw-r--r--kernel/sound/soc/codecs/tlv320aic3x.c62
-rw-r--r--kernel/sound/soc/codecs/tlv320dac33.c6
-rw-r--r--kernel/sound/soc/codecs/tpa6130a2.c15
-rw-r--r--kernel/sound/soc/codecs/ts3a227e.c63
-rw-r--r--kernel/sound/soc/codecs/twl4030.c23
-rw-r--r--kernel/sound/soc/codecs/twl6040.c9
-rw-r--r--kernel/sound/soc/codecs/uda134x.c190
-rw-r--r--kernel/sound/soc/codecs/uda134x.h2
-rw-r--r--kernel/sound/soc/codecs/uda1380.c21
-rw-r--r--kernel/sound/soc/codecs/wl1273.c9
-rw-r--r--kernel/sound/soc/codecs/wm0010.c33
-rw-r--r--kernel/sound/soc/codecs/wm1250-ev1.c3
-rw-r--r--kernel/sound/soc/codecs/wm2000.c5
-rw-r--r--kernel/sound/soc/codecs/wm2200.c11
-rw-r--r--kernel/sound/soc/codecs/wm5100.c13
-rw-r--r--kernel/sound/soc/codecs/wm5102.c96
-rw-r--r--kernel/sound/soc/codecs/wm5110.c523
-rw-r--r--kernel/sound/soc/codecs/wm8350.c10
-rw-r--r--kernel/sound/soc/codecs/wm8400.c8
-rw-r--r--kernel/sound/soc/codecs/wm8510.c6
-rw-r--r--kernel/sound/soc/codecs/wm8523.c31
-rw-r--r--kernel/sound/soc/codecs/wm8580.c5
-rw-r--r--kernel/sound/soc/codecs/wm8711.c5
-rw-r--r--kernel/sound/soc/codecs/wm8728.c5
-rw-r--r--kernel/sound/soc/codecs/wm8731.c97
-rw-r--r--kernel/sound/soc/codecs/wm8737.c14
-rw-r--r--kernel/sound/soc/codecs/wm8741.c248
-rw-r--r--kernel/sound/soc/codecs/wm8741.h10
-rw-r--r--kernel/sound/soc/codecs/wm8750.c5
-rw-r--r--kernel/sound/soc/codecs/wm8753.c18
-rw-r--r--kernel/sound/soc/codecs/wm8770.c4
-rw-r--r--kernel/sound/soc/codecs/wm8776.c9
-rw-r--r--kernel/sound/soc/codecs/wm8804-i2c.c1
-rw-r--r--kernel/sound/soc/codecs/wm8804-spi.c1
-rw-r--r--kernel/sound/soc/codecs/wm8804.c4
-rw-r--r--kernel/sound/soc/codecs/wm8900.c15
-rw-r--r--kernel/sound/soc/codecs/wm8903.c7
-rw-r--r--kernel/sound/soc/codecs/wm8904.c12
-rw-r--r--kernel/sound/soc/codecs/wm8940.c7
-rw-r--r--kernel/sound/soc/codecs/wm8955.c8
-rw-r--r--kernel/sound/soc/codecs/wm8958-dsp2.c8
-rw-r--r--kernel/sound/soc/codecs/wm8960.c310
-rw-r--r--kernel/sound/soc/codecs/wm8960.h1
-rw-r--r--kernel/sound/soc/codecs/wm8961.c14
-rw-r--r--kernel/sound/soc/codecs/wm8962.c56
-rw-r--r--kernel/sound/soc/codecs/wm8971.c4
-rw-r--r--kernel/sound/soc/codecs/wm8974.c5
-rw-r--r--kernel/sound/soc/codecs/wm8978.c8
-rw-r--r--kernel/sound/soc/codecs/wm8983.c85
-rw-r--r--kernel/sound/soc/codecs/wm8985.c5
-rw-r--r--kernel/sound/soc/codecs/wm8988.c5
-rw-r--r--kernel/sound/soc/codecs/wm8990.c11
-rw-r--r--kernel/sound/soc/codecs/wm8991.c56
-rw-r--r--kernel/sound/soc/codecs/wm8993.c24
-rw-r--r--kernel/sound/soc/codecs/wm8994.c90
-rw-r--r--kernel/sound/soc/codecs/wm8995.c10
-rw-r--r--kernel/sound/soc/codecs/wm8996.c22
-rw-r--r--kernel/sound/soc/codecs/wm8997.c36
-rw-r--r--kernel/sound/soc/codecs/wm8998.c1430
-rw-r--r--kernel/sound/soc/codecs/wm8998.h23
-rw-r--r--kernel/sound/soc/codecs/wm9081.c14
-rw-r--r--kernel/sound/soc/codecs/wm9090.c28
-rw-r--r--kernel/sound/soc/codecs/wm9705.c40
-rw-r--r--kernel/sound/soc/codecs/wm9712.c48
-rw-r--r--kernel/sound/soc/codecs/wm9713.c62
-rw-r--r--kernel/sound/soc/codecs/wm9713.h2
-rw-r--r--kernel/sound/soc/codecs/wm_adsp.c1452
-rw-r--r--kernel/sound/soc/codecs/wm_adsp.h33
-rw-r--r--kernel/sound/soc/codecs/wm_hubs.c11
-rw-r--r--kernel/sound/soc/codecs/wmfw.h44
-rw-r--r--kernel/sound/soc/davinci/davinci-i2s.c25
-rw-r--r--kernel/sound/soc/davinci/davinci-mcasp.c468
-rw-r--r--kernel/sound/soc/davinci/davinci-mcasp.h5
-rw-r--r--kernel/sound/soc/davinci/davinci-vcif.c14
-rw-r--r--kernel/sound/soc/dwc/designware_i2s.c142
-rw-r--r--kernel/sound/soc/fsl/Kconfig2
-rw-r--r--kernel/sound/soc/fsl/eukrea-tlv320.c2
-rw-r--r--kernel/sound/soc/fsl/fsl-asoc-card.c160
-rw-r--r--kernel/sound/soc/fsl/fsl_asrc.c25
-rw-r--r--kernel/sound/soc/fsl/fsl_dma.c4
-rw-r--r--kernel/sound/soc/fsl/fsl_esai.c92
-rw-r--r--kernel/sound/soc/fsl/fsl_sai.c201
-rw-r--r--kernel/sound/soc/fsl/fsl_sai.h24
-rw-r--r--kernel/sound/soc/fsl/fsl_spdif.c108
-rw-r--r--kernel/sound/soc/fsl/fsl_ssi.c184
-rw-r--r--kernel/sound/soc/fsl/imx-audmux.c2
-rw-r--r--kernel/sound/soc/fsl/imx-mc13783.c6
-rw-r--r--kernel/sound/soc/fsl/imx-pcm-dma.c25
-rw-r--r--kernel/sound/soc/fsl/imx-pcm.h9
-rw-r--r--kernel/sound/soc/fsl/imx-spdif.c1
-rw-r--r--kernel/sound/soc/fsl/imx-ssi.c21
-rw-r--r--kernel/sound/soc/fsl/mpc8610_hpcd.c2
-rw-r--r--kernel/sound/soc/fsl/p1022_ds.c2
-rw-r--r--kernel/sound/soc/fsl/p1022_rdk.c2
-rw-r--r--kernel/sound/soc/generic/simple-card.c51
-rw-r--r--kernel/sound/soc/intel/Kconfig63
-rw-r--r--kernel/sound/soc/intel/Makefile3
-rw-r--r--kernel/sound/soc/intel/atom/sst-atom-controls.c193
-rw-r--r--kernel/sound/soc/intel/atom/sst-atom-controls.h9
-rw-r--r--kernel/sound/soc/intel/atom/sst-mfld-platform-pcm.c67
-rw-r--r--kernel/sound/soc/intel/atom/sst-mfld-platform.h3
-rw-r--r--kernel/sound/soc/intel/atom/sst/sst_acpi.c4
-rw-r--r--kernel/sound/soc/intel/atom/sst/sst_drv_interface.c11
-rw-r--r--kernel/sound/soc/intel/atom/sst/sst_ipc.c3
-rw-r--r--kernel/sound/soc/intel/baytrail/sst-baytrail-ipc.c13
-rw-r--r--kernel/sound/soc/intel/boards/Makefile4
-rw-r--r--kernel/sound/soc/intel/boards/broadwell.c9
-rw-r--r--kernel/sound/soc/intel/boards/byt-max98090.c1
-rw-r--r--kernel/sound/soc/intel/boards/byt-rt5640.c1
-rw-r--r--kernel/sound/soc/intel/boards/bytcr_rt5640.c15
-rw-r--r--kernel/sound/soc/intel/boards/cht_bsw_max98090_ti.c335
-rw-r--r--kernel/sound/soc/intel/boards/cht_bsw_rt5645.c134
-rw-r--r--kernel/sound/soc/intel/boards/cht_bsw_rt5672.c15
-rw-r--r--kernel/sound/soc/intel/boards/skl_rt286.c259
-rw-r--r--kernel/sound/soc/intel/common/Makefile6
-rw-r--r--kernel/sound/soc/intel/common/sst-acpi.c2
-rw-r--r--kernel/sound/soc/intel/common/sst-dsp-priv.h24
-rw-r--r--kernel/sound/soc/intel/common/sst-dsp.c73
-rw-r--r--kernel/sound/soc/intel/common/sst-dsp.h8
-rw-r--r--kernel/sound/soc/intel/common/sst-firmware.c10
-rw-r--r--kernel/sound/soc/intel/common/sst-ipc.c34
-rw-r--r--kernel/sound/soc/intel/common/sst-ipc.h7
-rw-r--r--kernel/sound/soc/intel/haswell/sst-haswell-ipc.c34
-rw-r--r--kernel/sound/soc/intel/haswell/sst-haswell-pcm.c32
-rw-r--r--kernel/sound/soc/intel/skylake/Makefile10
-rw-r--r--kernel/sound/soc/intel/skylake/skl-messages.c918
-rw-r--r--kernel/sound/soc/intel/skylake/skl-nhlt.c140
-rw-r--r--kernel/sound/soc/intel/skylake/skl-nhlt.h106
-rw-r--r--kernel/sound/soc/intel/skylake/skl-pcm.c968
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst-cldma.c327
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst-cldma.h251
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst-dsp.c347
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst-dsp.h145
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst-ipc.c783
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst-ipc.h126
-rw-r--r--kernel/sound/soc/intel/skylake/skl-sst.c284
-rw-r--r--kernel/sound/soc/intel/skylake/skl-topology.c1254
-rw-r--r--kernel/sound/soc/intel/skylake/skl-topology.h318
-rw-r--r--kernel/sound/soc/intel/skylake/skl-tplg-interface.h171
-rw-r--r--kernel/sound/soc/intel/skylake/skl.c558
-rw-r--r--kernel/sound/soc/intel/skylake/skl.h97
-rw-r--r--kernel/sound/soc/jz4740/jz4740-i2s.c1
-rw-r--r--kernel/sound/soc/kirkwood/armada-370-db.c1
-rw-r--r--kernel/sound/soc/kirkwood/kirkwood-dma.c4
-rw-r--r--kernel/sound/soc/mediatek/Kconfig30
-rw-r--r--kernel/sound/soc/mediatek/Makefile5
-rw-r--r--kernel/sound/soc/mediatek/mt8173-max98090.c213
-rw-r--r--kernel/sound/soc/mediatek/mt8173-rt5650-rt5676.c280
-rw-r--r--kernel/sound/soc/mediatek/mtk-afe-common.h101
-rw-r--r--kernel/sound/soc/mediatek/mtk-afe-pcm.c1317
-rw-r--r--kernel/sound/soc/mxs/mxs-sgtl5000.c6
-rw-r--r--kernel/sound/soc/nuc900/nuc900-pcm.c9
-rw-r--r--kernel/sound/soc/omap/Kconfig2
-rw-r--r--kernel/sound/soc/omap/mcbsp.c20
-rw-r--r--kernel/sound/soc/omap/n810.c3
-rw-r--r--kernel/sound/soc/omap/omap-hdmi-audio.c22
-rw-r--r--kernel/sound/soc/omap/omap-twl4030.c3
-rw-r--r--kernel/sound/soc/omap/omap3pandora.c6
-rw-r--r--kernel/sound/soc/omap/rx51.c45
-rw-r--r--kernel/sound/soc/pxa/Kconfig2
-rw-r--r--kernel/sound/soc/pxa/brownstone.c34
-rw-r--r--kernel/sound/soc/pxa/corgi.c11
-rw-r--r--kernel/sound/soc/pxa/e740_wm9705.c5
-rw-r--r--kernel/sound/soc/pxa/e750_wm9705.c5
-rw-r--r--kernel/sound/soc/pxa/e800_wm9712.c5
-rw-r--r--kernel/sound/soc/pxa/hx4700.c4
-rw-r--r--kernel/sound/soc/pxa/imote2.c11
-rw-r--r--kernel/sound/soc/pxa/mioa701_wm9713.c11
-rw-r--r--kernel/sound/soc/pxa/mmp-pcm.c9
-rw-r--r--kernel/sound/soc/pxa/palm27x.c9
-rw-r--r--kernel/sound/soc/pxa/poodle.c30
-rw-r--r--kernel/sound/soc/pxa/pxa-ssp.c12
-rw-r--r--kernel/sound/soc/pxa/pxa2xx-ac97.c49
-rw-r--r--kernel/sound/soc/pxa/pxa2xx-i2s.c14
-rw-r--r--kernel/sound/soc/pxa/pxa2xx-pcm.c42
-rw-r--r--kernel/sound/soc/pxa/spitz.c5
-rw-r--r--kernel/sound/soc/pxa/tosa.c18
-rw-r--r--kernel/sound/soc/pxa/ttc-dkb.c12
-rw-r--r--kernel/sound/soc/pxa/z2.c9
-rw-r--r--kernel/sound/soc/qcom/Kconfig25
-rw-r--r--kernel/sound/soc/qcom/Makefile6
-rw-r--r--kernel/sound/soc/qcom/apq8016_sbc.c198
-rw-r--r--kernel/sound/soc/qcom/lpass-apq8016.c242
-rw-r--r--kernel/sound/soc/qcom/lpass-cpu.c241
-rw-r--r--kernel/sound/soc/qcom/lpass-ipq806x.c109
-rw-r--r--kernel/sound/soc/qcom/lpass-lpaif-reg.h (renamed from kernel/sound/soc/qcom/lpass-lpaif-ipq806x.h)92
-rw-r--r--kernel/sound/soc/qcom/lpass-platform.c202
-rw-r--r--kernel/sound/soc/qcom/lpass.h51
-rw-r--r--kernel/sound/soc/qcom/storm.c26
-rw-r--r--kernel/sound/soc/rockchip/Kconfig27
-rw-r--r--kernel/sound/soc/rockchip/Makefile12
-rw-r--r--kernel/sound/soc/rockchip/rockchip_i2s.c56
-rw-r--r--kernel/sound/soc/rockchip/rockchip_i2s.h16
-rw-r--r--kernel/sound/soc/rockchip/rockchip_max98090.c236
-rw-r--r--kernel/sound/soc/rockchip/rockchip_rt5645.c225
-rw-r--r--kernel/sound/soc/rockchip/rockchip_spdif.c407
-rw-r--r--kernel/sound/soc/rockchip/rockchip_spdif.h63
-rw-r--r--kernel/sound/soc/samsung/Kconfig15
-rw-r--r--kernel/sound/soc/samsung/arndale_rt5631.c1
-rw-r--r--kernel/sound/soc/samsung/h1940_uda1380.c5
-rw-r--r--kernel/sound/soc/samsung/i2s.c23
-rw-r--r--kernel/sound/soc/samsung/lowland.c2
-rw-r--r--kernel/sound/soc/samsung/rx1950_uda1380.c5
-rw-r--r--kernel/sound/soc/samsung/smartq_wm8987.c6
-rw-r--r--kernel/sound/soc/samsung/smdk_wm8994.c3
-rw-r--r--kernel/sound/soc/samsung/snow.c1
-rw-r--r--kernel/sound/soc/samsung/speyside.c2
-rw-r--r--kernel/sound/soc/sh/Kconfig3
-rw-r--r--kernel/sound/soc/sh/dma-sh7760.c9
-rw-r--r--kernel/sound/soc/sh/fsi.c1
-rw-r--r--kernel/sound/soc/sh/migor.c3
-rw-r--r--kernel/sound/soc/sh/rcar/Makefile2
-rw-r--r--kernel/sound/soc/sh/rcar/adg.c306
-rw-r--r--kernel/sound/soc/sh/rcar/core.c372
-rw-r--r--kernel/sound/soc/sh/rcar/ctu.c169
-rw-r--r--kernel/sound/soc/sh/rcar/dma.c241
-rw-r--r--kernel/sound/soc/sh/rcar/dvc.c103
-rw-r--r--kernel/sound/soc/sh/rcar/gen.c49
-rw-r--r--kernel/sound/soc/sh/rcar/mix.c198
-rw-r--r--kernel/sound/soc/sh/rcar/rcar_snd.h117
-rw-r--r--kernel/sound/soc/sh/rcar/rsnd.h235
-rw-r--r--kernel/sound/soc/sh/rcar/rsrc-card.c457
-rw-r--r--kernel/sound/soc/sh/rcar/src.c267
-rw-r--r--kernel/sound/soc/sh/rcar/ssi.c265
-rw-r--r--kernel/sound/soc/sh/siu_dai.c85
-rw-r--r--kernel/sound/soc/sh/ssi.c12
-rw-r--r--kernel/sound/soc/soc-ac97.c30
-rw-r--r--kernel/sound/soc/soc-compress.c35
-rw-r--r--kernel/sound/soc/soc-core.c189
-rw-r--r--kernel/sound/soc/soc-dapm.c890
-rw-r--r--kernel/sound/soc/soc-generic-dmaengine-pcm.c25
-rw-r--r--kernel/sound/soc/soc-jack.c12
-rw-r--r--kernel/sound/soc/soc-ops.c38
-rw-r--r--kernel/sound/soc/soc-pcm.c125
-rw-r--r--kernel/sound/soc/soc-topology.c1860
-rw-r--r--kernel/sound/soc/soc-utils.c9
-rw-r--r--kernel/sound/soc/spear/Kconfig2
-rw-r--r--kernel/sound/soc/spear/spdif_in.c20
-rw-r--r--kernel/sound/soc/spear/spear_pcm.c2
-rw-r--r--kernel/sound/soc/sti/Kconfig11
-rw-r--r--kernel/sound/soc/sti/Makefile4
-rw-r--r--kernel/sound/soc/sti/sti_uniperif.c254
-rw-r--r--kernel/sound/soc/sti/uniperif.h1229
-rw-r--r--kernel/sound/soc/sti/uniperif_player.c1119
-rw-r--r--kernel/sound/soc/sti/uniperif_reader.c365
-rw-r--r--kernel/sound/soc/sunxi/Kconfig11
-rw-r--r--kernel/sound/soc/sunxi/Makefile2
-rw-r--r--kernel/sound/soc/sunxi/sun4i-codec.c713
-rw-r--r--kernel/sound/soc/tegra/tegra20_das.c23
-rw-r--r--kernel/sound/soc/tegra/tegra20_i2s.c23
-rw-r--r--kernel/sound/soc/tegra/tegra20_spdif.c47
-rw-r--r--kernel/sound/soc/tegra/tegra30_ahub.c80
-rw-r--r--kernel/sound/soc/tegra/tegra30_i2s.c23
-rw-r--r--kernel/sound/soc/txx9/txx9aclc.c10
-rw-r--r--kernel/sound/soc/ux500/mop500.c1
-rw-r--r--kernel/sound/soc/ux500/mop500_ab8500.c4
-rw-r--r--kernel/sound/soc/ux500/ux500_msp_dai.c34
-rw-r--r--kernel/sound/soc/ux500/ux500_pcm.c1
-rw-r--r--kernel/sound/soc/xtensa/xtfpga-i2s.c8
-rw-r--r--kernel/sound/soc/zte/Kconfig17
-rw-r--r--kernel/sound/soc/zte/Makefile2
-rw-r--r--kernel/sound/soc/zte/zx296702-i2s.c436
-rw-r--r--kernel/sound/soc/zte/zx296702-spdif.c365
-rw-r--r--kernel/sound/sound_core.c2
-rw-r--r--kernel/sound/sound_firmware.c4
-rw-r--r--kernel/sound/sparc/Kconfig1
-rw-r--r--kernel/sound/sparc/amd7930.c2
-rw-r--r--kernel/sound/synth/emux/Makefile5
-rw-r--r--kernel/sound/synth/emux/emux.c4
-rw-r--r--kernel/sound/synth/emux/emux_oss.c3
-rw-r--r--kernel/sound/synth/emux/emux_proc.c4
-rw-r--r--kernel/sound/synth/emux/emux_voice.h6
-rw-r--r--kernel/sound/usb/bcd2000/bcd2000.c2
-rw-r--r--kernel/sound/usb/card.c110
-rw-r--r--kernel/sound/usb/card.h1
-rw-r--r--kernel/sound/usb/endpoint.c102
-rw-r--r--kernel/sound/usb/midi.c58
-rw-r--r--kernel/sound/usb/mixer.c114
-rw-r--r--kernel/sound/usb/mixer.h2
-rw-r--r--kernel/sound/usb/mixer_maps.c12
-rw-r--r--kernel/sound/usb/mixer_quirks.c167
-rw-r--r--kernel/sound/usb/mixer_quirks.h4
-rw-r--r--kernel/sound/usb/pcm.c138
-rw-r--r--kernel/sound/usb/proc.c4
-rw-r--r--kernel/sound/usb/quirks-table.h34
-rw-r--r--kernel/sound/usb/quirks.c24
-rw-r--r--kernel/sound/usb/stream.c11
-rw-r--r--kernel/sound/usb/usbaudio.h13
706 files changed, 55368 insertions, 13751 deletions
diff --git a/kernel/sound/ac97_bus.c b/kernel/sound/ac97_bus.c
index 2b50cbe6a..52e4bc54c 100644
--- a/kernel/sound/ac97_bus.c
+++ b/kernel/sound/ac97_bus.c
@@ -18,44 +18,80 @@
#include <sound/ac97_codec.h>
/*
- * Let drivers decide whether they want to support given codec from their
- * probe method. Drivers have direct access to the struct snd_ac97
- * structure and may decide based on the id field amongst other things.
+ * snd_ac97_check_id() - Reads and checks the vendor ID of the device
+ * @ac97: The AC97 device to check
+ * @id: The ID to compare to
+ * @id_mask: Mask that is applied to the device ID before comparing to @id
+ *
+ * If @id is 0 this function returns true if the read device vendor ID is
+ * a valid ID. If @id is non 0 this functions returns true if @id
+ * matches the read vendor ID. Otherwise the function returns false.
*/
-static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id,
+ unsigned int id_mask)
{
- return 1;
-}
+ ac97->id = ac97->bus->ops->read(ac97, AC97_VENDOR_ID1) << 16;
+ ac97->id |= ac97->bus->ops->read(ac97, AC97_VENDOR_ID2);
-#ifdef CONFIG_PM
-static int ac97_bus_suspend(struct device *dev, pm_message_t state)
-{
- int ret = 0;
+ if (ac97->id == 0x0 || ac97->id == 0xffffffff)
+ return false;
- if (dev->driver && dev->driver->suspend)
- ret = dev->driver->suspend(dev, state);
+ if (id != 0 && id != (ac97->id & id_mask))
+ return false;
- return ret;
+ return true;
}
-static int ac97_bus_resume(struct device *dev)
+/**
+ * snd_ac97_reset() - Reset AC'97 device
+ * @ac97: The AC'97 device to reset
+ * @try_warm: Try a warm reset first
+ * @id: Expected device vendor ID
+ * @id_mask: Mask that is applied to the device ID before comparing to @id
+ *
+ * This function resets the AC'97 device. If @try_warm is true the function
+ * first performs a warm reset. If the warm reset is successful the function
+ * returns 1. Otherwise or if @try_warm is false the function issues cold reset
+ * followed by a warm reset. If this is successful the function returns 0,
+ * otherwise a negative error code. If @id is 0 any valid device ID will be
+ * accepted, otherwise only the ID that matches @id and @id_mask is accepted.
+ */
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+ unsigned int id_mask)
{
- int ret = 0;
+ struct snd_ac97_bus_ops *ops = ac97->bus->ops;
+
+ if (try_warm && ops->warm_reset) {
+ ops->warm_reset(ac97);
+ if (snd_ac97_check_id(ac97, id, id_mask))
+ return 1;
+ }
- if (dev->driver && dev->driver->resume)
- ret = dev->driver->resume(dev);
+ if (ops->reset)
+ ops->reset(ac97);
+ if (ops->warm_reset)
+ ops->warm_reset(ac97);
- return ret;
+ if (snd_ac97_check_id(ac97, id, id_mask))
+ return 0;
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
+
+/*
+ * Let drivers decide whether they want to support given codec from their
+ * probe method. Drivers have direct access to the struct snd_ac97
+ * structure and may decide based on the id field amongst other things.
+ */
+static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
}
-#endif /* CONFIG_PM */
struct bus_type ac97_bus_type = {
.name = "ac97",
.match = ac97_bus_match,
-#ifdef CONFIG_PM
- .suspend = ac97_bus_suspend,
- .resume = ac97_bus_resume,
-#endif /* CONFIG_PM */
};
static int __init ac97_bus_init(void)
diff --git a/kernel/sound/aoa/codecs/onyx.c b/kernel/sound/aoa/codecs/onyx.c
index 23c371ecf..a04edff8b 100644
--- a/kernel/sound/aoa/codecs/onyx.c
+++ b/kernel/sound/aoa/codecs/onyx.c
@@ -1050,7 +1050,6 @@ MODULE_DEVICE_TABLE(i2c,onyx_i2c_id);
static struct i2c_driver onyx_driver = {
.driver = {
.name = "aoa_codec_onyx",
- .owner = THIS_MODULE,
},
.probe = onyx_i2c_probe,
.remove = onyx_i2c_remove,
diff --git a/kernel/sound/aoa/codecs/tas.c b/kernel/sound/aoa/codecs/tas.c
index 364c7c441..78ed1ffbf 100644
--- a/kernel/sound/aoa/codecs/tas.c
+++ b/kernel/sound/aoa/codecs/tas.c
@@ -939,7 +939,6 @@ MODULE_DEVICE_TABLE(i2c,tas_i2c_id);
static struct i2c_driver tas_driver = {
.driver = {
.name = "aoa_codec_tas",
- .owner = THIS_MODULE,
},
.probe = tas_i2c_probe,
.remove = tas_i2c_remove,
diff --git a/kernel/sound/aoa/fabrics/layout.c b/kernel/sound/aoa/fabrics/layout.c
index 9dc5806d2..8f71f7e4d 100644
--- a/kernel/sound/aoa/fabrics/layout.c
+++ b/kernel/sound/aoa/fabrics/layout.c
@@ -1120,10 +1120,10 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
return 0;
}
-#ifdef CONFIG_PM
-static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int aoa_fabric_layout_suspend(struct device *dev)
{
- struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
+ struct layout_dev *ldev = dev_get_drvdata(dev);
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
ldev->gpio.methods->all_amps_off(&ldev->gpio);
@@ -1131,15 +1131,19 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta
return 0;
}
-static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
+static int aoa_fabric_layout_resume(struct device *dev)
{
- struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
+ struct layout_dev *ldev = dev_get_drvdata(dev);
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
ldev->gpio.methods->all_amps_restore(&ldev->gpio);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
+ aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
+
#endif
static struct soundbus_driver aoa_soundbus_driver = {
@@ -1147,12 +1151,11 @@ static struct soundbus_driver aoa_soundbus_driver = {
.owner = THIS_MODULE,
.probe = aoa_fabric_layout_probe,
.remove = aoa_fabric_layout_remove,
-#ifdef CONFIG_PM
- .suspend = aoa_fabric_layout_suspend,
- .resume = aoa_fabric_layout_resume,
-#endif
.driver = {
.owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &aoa_fabric_layout_pm_ops,
+#endif
}
};
diff --git a/kernel/sound/aoa/soundbus/core.c b/kernel/sound/aoa/soundbus/core.c
index 7487eb76e..70bcaa7f9 100644
--- a/kernel/sound/aoa/soundbus/core.c
+++ b/kernel/sound/aoa/soundbus/core.c
@@ -126,41 +126,15 @@ static void soundbus_device_shutdown(struct device *dev)
drv->shutdown(soundbus_dev);
}
-#ifdef CONFIG_PM
-
-static int soundbus_device_suspend(struct device *dev, pm_message_t state)
-{
- struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
- struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
-
- if (dev->driver && drv->suspend)
- return drv->suspend(soundbus_dev, state);
- return 0;
-}
-
-static int soundbus_device_resume(struct device * dev)
-{
- struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
- struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
-
- if (dev->driver && drv->resume)
- return drv->resume(soundbus_dev);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
+/* soundbus_dev_attrs is declared in sysfs.c */
+ATTRIBUTE_GROUPS(soundbus_dev);
static struct bus_type soundbus_bus_type = {
.name = "aoa-soundbus",
.probe = soundbus_probe,
.uevent = soundbus_uevent,
.remove = soundbus_device_remove,
.shutdown = soundbus_device_shutdown,
-#ifdef CONFIG_PM
- .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/kernel/sound/aoa/soundbus/soundbus.h b/kernel/sound/aoa/soundbus/soundbus.h
index adecbf36f..ae4022438 100644
--- a/kernel/sound/aoa/soundbus/soundbus.h
+++ b/kernel/sound/aoa/soundbus/soundbus.h
@@ -188,8 +188,6 @@ struct soundbus_driver {
int (*probe)(struct soundbus_dev* dev);
int (*remove)(struct soundbus_dev* dev);
- int (*suspend)(struct soundbus_dev* dev, pm_message_t state);
- int (*resume)(struct soundbus_dev* dev);
int (*shutdown)(struct soundbus_dev* dev);
struct device_driver driver;
@@ -199,6 +197,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/kernel/sound/aoa/soundbus/sysfs.c b/kernel/sound/aoa/soundbus/sysfs.c
index e0980b5c2..5b2d51d99 100644
--- a/kernel/sound/aoa/soundbus/sysfs.c
+++ b/kernel/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/kernel/sound/arm/Kconfig b/kernel/sound/arm/Kconfig
index 885683a3b..e04062117 100644
--- a/kernel/sound/arm/Kconfig
+++ b/kernel/sound/arm/Kconfig
@@ -9,6 +9,14 @@ menuconfig SND_ARM
Drivers that are implemented on ASoC can be found in
"ALSA for SoC audio support" section.
+config SND_PXA2XX_LIB
+ tristate
+ select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
+ select SND_DMAENGINE_PCM
+
+config SND_PXA2XX_LIB_AC97
+ bool
+
if SND_ARM
config SND_ARMAACI
@@ -21,13 +29,6 @@ config SND_PXA2XX_PCM
tristate
select SND_PCM
-config SND_PXA2XX_LIB
- tristate
- select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
-
-config SND_PXA2XX_LIB_AC97
- bool
-
config SND_PXA2XX_AC97
tristate "AC97 driver for the Intel PXA2xx chip"
depends on ARCH_PXA
diff --git a/kernel/sound/arm/pxa2xx-ac97.c b/kernel/sound/arm/pxa2xx-ac97.c
index 38590b322..fbd5dad0c 100644
--- a/kernel/sound/arm/pxa2xx-ac97.c
+++ b/kernel/sound/arm/pxa2xx-ac97.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_reset,
};
-static unsigned long pxa2xx_ac97_pcm_out_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 12,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.filter_data = &pxa2xx_ac97_pcm_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_in_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 11,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
diff --git a/kernel/sound/arm/pxa2xx-pcm-lib.c b/kernel/sound/arm/pxa2xx-pcm-lib.c
index 01f8fdc42..e9b98af6b 100644
--- a/kernel/sound/arm/pxa2xx-pcm-lib.c
+++ b/kernel/sound/arm/pxa2xx-pcm-lib.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -15,8 +16,6 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include <mach/dma.h>
-
#include "pxa2xx-pcm.h"
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
@@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.period_bytes_min = 32,
.period_bytes_max = 8192 - 32,
.periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .periods_max = 256,
.buffer_bytes_max = 128 * 1024,
.fifo_size = 32,
};
@@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- size_t totsize = params_buffer_bytes(params);
- size_t period = params_period_bytes(params);
- pxa_dma_desc *dma_desc;
- dma_addr_t dma_buff_phys, next_desc_phys;
- u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct dma_slave_config config;
+ int ret;
- /* temporary transition hack */
- switch (rtd->params->addr_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- dcmd |= DCMD_WIDTH1;
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- dcmd |= DCMD_WIDTH2;
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- dcmd |= DCMD_WIDTH4;
- break;
- default:
- /* can't happen */
- break;
- }
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dma_params)
+ return 0;
- switch (rtd->params->maxburst) {
- case 8:
- dcmd |= DCMD_BURST8;
- break;
- case 16:
- dcmd |= DCMD_BURST16;
- break;
- case 32:
- dcmd |= DCMD_BURST32;
- break;
- }
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
+ if (ret)
+ return ret;
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
+ snd_dmaengine_pcm_set_config_from_dai_data(substream,
+ snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
+ &config);
- dma_desc = rtd->dma_desc_array;
- next_desc_phys = rtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- dma_desc->ddadr = next_desc_phys;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = rtd->params->addr;
- } else {
- dma_desc->dsadr = rtd->params->addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret)
+ return ret;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
@@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
- if (rtd && rtd->params && rtd->params->filter_data) {
- unsigned long req = *(unsigned long *) rtd->params->filter_data;
- DRCMR(req) = 0;
- }
-
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
+ return snd_dmaengine_pcm_trigger(substream, cmd);
}
EXPORT_SYMBOL(pxa2xx_pcm_trigger);
snd_pcm_uframes_t
pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
- if (x == runtime->buffer_size)
- x = 0;
- return x;
+ return snd_dmaengine_pcm_pointer(substream);
}
EXPORT_SYMBOL(pxa2xx_pcm_pointer);
int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- unsigned long req;
-
- if (!prtd || !prtd->params)
- return 0;
-
- if (prtd->dma_ch == -1)
- return -EINVAL;
-
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- DCSR(prtd->dma_ch) = 0;
- DCMD(prtd->dma_ch) = 0;
- req = *(unsigned long *) prtd->params->filter_data;
- DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
-
return 0;
}
EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
-void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- int dcsr;
-
- dcsr = DCSR(dma_ch);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- if (dcsr & DCSR_ENDINTR) {
- snd_pcm_period_elapsed(substream);
- } else {
- printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
- dma_ch, dcsr);
- snd_pcm_stop_xrun(substream);
- }
-}
-EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
-
int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd;
+ struct snd_dmaengine_dai_dma_data *dma_params;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dma_params)
+ return 0;
+
/*
* For mysterious reasons (and despite what the manual says)
* playback samples are lost if the DMA count is not a multiple
@@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
- goto out;
-
- ret = -ENOMEM;
- rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
- if (!rtd)
- goto out;
- rtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &rtd->dma_desc_array_phys, GFP_KERNEL);
- if (!rtd->dma_desc_array)
- goto err1;
+ return ret;
- rtd->dma_ch = -1;
- runtime->private_data = rtd;
- return 0;
-
- err1:
- kfree(rtd);
- out:
- return ret;
+ return snd_dmaengine_pcm_open_request_chan(substream,
+ pxad_filter_fn,
+ dma_params->filter_data);
}
EXPORT_SYMBOL(__pxa2xx_pcm_open);
int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
-
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
+ return snd_dmaengine_pcm_close_release_chan(substream);
}
EXPORT_SYMBOL(__pxa2xx_pcm_close);
diff --git a/kernel/sound/arm/pxa2xx-pcm.c b/kernel/sound/arm/pxa2xx-pcm.c
index 83be8e3f0..83fcfac97 100644
--- a/kernel/sound/arm/pxa2xx-pcm.c
+++ b/kernel/sound/arm/pxa2xx-pcm.c
@@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
client->playback_params : client->capture_params;
- ret = pxa_request_dma("dma", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- goto err2;
- rtd->dma_ch = ret;
ret = client->startup(substream);
if (!ret)
- goto out;
+ goto err2;
+
+ return 0;
- pxa_free_dma(rtd->dma_ch);
err2:
__pxa2xx_pcm_close(substream);
out:
@@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- pxa_free_dma(rtd->dma_ch);
client->shutdown(substream);
return __pxa2xx_pcm_close(substream);
diff --git a/kernel/sound/arm/pxa2xx-pcm.h b/kernel/sound/arm/pxa2xx-pcm.h
index 00330985b..8fa2b7c9e 100644
--- a/kernel/sound/arm/pxa2xx-pcm.h
+++ b/kernel/sound/arm/pxa2xx-pcm.h
@@ -13,8 +13,6 @@
struct pxa2xx_runtime_data {
int dma_ch;
struct snd_dmaengine_dai_dma_data *params;
- struct pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
};
struct pxa2xx_pcm_client {
diff --git a/kernel/sound/core/Kconfig b/kernel/sound/core/Kconfig
index 313f22e9d..e3e949126 100644
--- a/kernel/sound/core/Kconfig
+++ b/kernel/sound/core/Kconfig
@@ -4,7 +4,13 @@ config SND_TIMER
config SND_PCM
tristate
- select SND_TIMER
+ select SND_TIMER if SND_PCM_TIMER
+
+config SND_PCM_ELD
+ bool
+
+config SND_PCM_IEC958
+ bool
config SND_DMAENGINE_PCM
tristate
@@ -87,6 +93,17 @@ config SND_PCM_OSS_PLUGINS
support conversion of channels, formats and rates. It will
behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
+config SND_PCM_TIMER
+ bool "PCM timer interface" if EXPERT
+ default y
+ help
+ If you disable this option, pcm timer will be inavailable, so
+ those stubs used pcm timer (e.g. dmix, dsnoop & co) may work
+ incorrectlly.
+
+ For some embedded device, we may disable it to reduce memory
+ footprint, about 20KB on x86_64 platform.
+
config SND_SEQUENCER_OSS
bool "OSS Sequencer API"
depends on SND_SEQUENCER
@@ -176,9 +193,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 +247,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/kernel/sound/core/Makefile b/kernel/sound/core/Makefile
index 4daf2f582..48ab4b8f8 100644
--- a/kernel/sound/core/Makefile
+++ b/kernel/sound/core/Makefile
@@ -3,16 +3,22 @@
# 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 \
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.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/kernel/sound/core/compress_offload.c b/kernel/sound/core/compress_offload.c
index b123c42e7..b554d7f9e 100644
--- a/kernel/sound/core/compress_offload.c
+++ b/kernel/sound/core/compress_offload.c
@@ -44,6 +44,13 @@
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
+/* struct snd_compr_codec_caps overflows the ioctl bit size for some
+ * architectures, so we need to disable the relevant ioctls.
+ */
+#if _IOC_SIZEBITS < 14
+#define COMPR_CODEC_CAPS_OVERFLOW
+#endif
+
/* TODO:
* - add substream support for multiple devices in case of
* SND_DYNAMIC_MINORS is not used
@@ -438,6 +445,7 @@ out:
return retval;
}
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
static int
snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
{
@@ -461,6 +469,7 @@ out:
kfree(caps);
return retval;
}
+#endif /* !COMPR_CODEC_CAPS_OVERFLOW */
/* revisit this with snd_pcm_preallocate_xxx */
static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
@@ -799,9 +808,11 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
retval = snd_compr_get_caps(stream, arg);
break;
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
retval = snd_compr_get_codec_caps(stream, arg);
break;
+#endif
case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
retval = snd_compr_set_params(stream, arg);
break;
diff --git a/kernel/sound/core/control.c b/kernel/sound/core/control.c
index 196a6fe10..a85d45595 100644
--- a/kernel/sound/core/control.c
+++ b/kernel/sound/core/control.c
@@ -1405,6 +1405,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
return -EFAULT;
if (tlv.length < sizeof(unsigned int) * 2)
return -EINVAL;
+ if (!tlv.numid)
+ return -EINVAL;
down_read(&card->controls_rwsem);
kctl = snd_ctl_find_numid(card, tlv.numid);
if (kctl == NULL) {
diff --git a/kernel/sound/core/control_compat.c b/kernel/sound/core/control_compat.c
index b9c0910fb..0608f216f 100644
--- a/kernel/sound/core/control_compat.c
+++ b/kernel/sound/core/control_compat.c
@@ -170,6 +170,19 @@ struct snd_ctl_elem_value32 {
unsigned char reserved[128];
};
+#ifdef CONFIG_X86_X32
+/* x32 has a different alignment for 64bit values from ia32 */
+struct snd_ctl_elem_value_x32 {
+ struct snd_ctl_elem_id id;
+ unsigned int indirect; /* bit-field causes misalignment */
+ union {
+ s32 integer[128];
+ unsigned char data[512];
+ s64 integer64[64];
+ } value;
+ unsigned char reserved[128];
+};
+#endif /* CONFIG_X86_X32 */
/* get the value type and count of the control */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
@@ -219,9 +232,11 @@ static int get_elem_size(int type, int count)
static int copy_ctl_value_from_user(struct snd_card *card,
struct snd_ctl_elem_value *data,
- struct snd_ctl_elem_value32 __user *data32,
+ void __user *userdata,
+ void __user *valuep,
int *typep, int *countp)
{
+ struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, type, size;
int uninitialized_var(count);
unsigned int indirect;
@@ -239,8 +254,9 @@ static int copy_ctl_value_from_user(struct snd_card *card,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
- if (get_user(val, &data32->value.integer[i]))
+ if (get_user(val, &intp[i]))
return -EFAULT;
data->value.integer.value[i] = val;
}
@@ -250,8 +266,7 @@ static int copy_ctl_value_from_user(struct snd_card *card,
dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL;
}
- if (copy_from_user(data->value.bytes.data,
- data32->value.data, size))
+ if (copy_from_user(data->value.bytes.data, valuep, size))
return -EFAULT;
}
@@ -261,7 +276,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
}
/* restore the value to 32bit */
-static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
+static int copy_ctl_value_to_user(void __user *userdata,
+ void __user *valuep,
struct snd_ctl_elem_value *data,
int type, int count)
{
@@ -270,22 +286,22 @@ static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
val = data->value.integer.value[i];
- if (put_user(val, &data32->value.integer[i]))
+ if (put_user(val, &intp[i]))
return -EFAULT;
}
} else {
size = get_elem_size(type, count);
- if (copy_to_user(data32->value.data,
- data->value.bytes.data, size))
+ if (copy_to_user(valuep, data->value.bytes.data, size))
return -EFAULT;
}
return 0;
}
-static int snd_ctl_elem_read_user_compat(struct snd_card *card,
- struct snd_ctl_elem_value32 __user *data32)
+static int ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
{
struct snd_ctl_elem_value *data;
int err, type, count;
@@ -294,7 +310,9 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
+ err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count);
+ if (err < 0)
goto error;
snd_power_lock(card);
@@ -303,14 +321,15 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
err = snd_ctl_elem_read(card, data);
snd_power_unlock(card);
if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
+ err = copy_ctl_value_to_user(userdata, valuep, data,
+ type, count);
error:
kfree(data);
return err;
}
-static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
- struct snd_ctl_elem_value32 __user *data32)
+static int ctl_elem_write_user(struct snd_ctl_file *file,
+ void __user *userdata, void __user *valuep)
{
struct snd_ctl_elem_value *data;
struct snd_card *card = file->card;
@@ -320,7 +339,9 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
+ err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count);
+ if (err < 0)
goto error;
snd_power_lock(card);
@@ -329,12 +350,39 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
err = snd_ctl_elem_write(card, file, data);
snd_power_unlock(card);
if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
+ err = copy_ctl_value_to_user(userdata, valuep, data,
+ type, count);
error:
kfree(data);
return err;
}
+static int snd_ctl_elem_read_user_compat(struct snd_card *card,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+
+#ifdef CONFIG_X86_X32
+static int snd_ctl_elem_read_user_x32(struct snd_card *card,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+#endif /* CONFIG_X86_X32 */
+
/* add or replace a user control */
static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32,
@@ -393,6 +441,10 @@ enum {
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
+#ifdef CONFIG_X86_X32
+ SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
+ SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
+#endif /* CONFIG_X86_X32 */
};
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -431,6 +483,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_ctl_elem_add_compat(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
return snd_ctl_elem_add_compat(ctl, argp, 1);
+#ifdef CONFIG_X86_X32
+ case SNDRV_CTL_IOCTL_ELEM_READ_X32:
+ return snd_ctl_elem_read_user_x32(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
+ return snd_ctl_elem_write_user_x32(ctl, argp);
+#endif /* CONFIG_X86_X32 */
}
down_read(&snd_ioctl_rwsem);
diff --git a/kernel/sound/core/ctljack.c b/kernel/sound/core/ctljack.c
index e4b38fbe5..84a3cd683 100644
--- a/kernel/sound/core/ctljack.c
+++ b/kernel/sound/core/ctljack.c
@@ -31,19 +31,52 @@ 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++;
+ /* reset numid; otherwise snd_ctl_find_id() hits this again */
+ sid.numid = 0;
+ }
+
+ 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 +86,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/kernel/sound/core/hrtimer.c b/kernel/sound/core/hrtimer.c
index 886be7da9..656d9a903 100644
--- a/kernel/sound/core/hrtimer.c
+++ b/kernel/sound/core/hrtimer.c
@@ -90,7 +90,7 @@ static int snd_hrtimer_start(struct snd_timer *t)
struct snd_hrtimer *stime = t->private_data;
atomic_set(&stime->running, 0);
- hrtimer_cancel(&stime->hrt);
+ hrtimer_try_to_cancel(&stime->hrt);
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL);
atomic_set(&stime->running, 1);
@@ -101,6 +101,7 @@ static int snd_hrtimer_stop(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
atomic_set(&stime->running, 0);
+ hrtimer_try_to_cancel(&stime->hrt);
return 0;
}
@@ -121,16 +122,9 @@ static struct snd_timer *mytimer;
static int __init snd_hrtimer_init(void)
{
struct snd_timer *timer;
- struct timespec tp;
int err;
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec > 0 || !tp.tv_nsec) {
- pr_err("snd-hrtimer: Invalid resolution %u.%09u",
- (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
- return -EINVAL;
- }
- resolution = tp.tv_nsec;
+ resolution = hrtimer_resolution;
/* Create a new timer and set up the fields */
err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
diff --git a/kernel/sound/core/hwdep.c b/kernel/sound/core/hwdep.c
index 51692c8a3..36d2416f9 100644
--- a/kernel/sound/core/hwdep.c
+++ b/kernel/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/kernel/sound/core/info.c b/kernel/sound/core/info.c
index 9f404e965..895362a69 100644
--- a/kernel/sound/core/info.c
+++ b/kernel/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/kernel/sound/core/info_oss.c b/kernel/sound/core/info_oss.c
index 83c29dbff..1478c8dfd 100644
--- a/kernel/sound/core/info_oss.c
+++ b/kernel/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/kernel/sound/core/init.c b/kernel/sound/core/init.c
index 04734e047..20f37fb38 100644
--- a/kernel/sound/core/init.c
+++ b/kernel/sound/core/init.c
@@ -100,35 +100,28 @@ 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;
+ return -ENOMEM;
}
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 +749,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 +775,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 +801,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 +822,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 +846,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/kernel/sound/core/jack.c b/kernel/sound/core/jack.c
index 8658578eb..7237acbdc 100644
--- a/kernel/sound/core/jack.c
+++ b/kernel/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/kernel/sound/core/memalloc.c b/kernel/sound/core/memalloc.c
index 082509eb8..f05cb6a8c 100644
--- a/kernel/sound/core/memalloc.c
+++ b/kernel/sound/core/memalloc.c
@@ -124,7 +124,7 @@ static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
dmab->addr = 0;
if (dev->of_node)
- pool = of_get_named_gen_pool(dev->of_node, "iram", 0);
+ pool = of_gen_pool_get(dev->of_node, "iram", 0);
if (!pool)
return;
diff --git a/kernel/sound/core/oss/mixer_oss.c b/kernel/sound/core/oss/mixer_oss.c
index 056f8e274..7a8c79dd9 100644
--- a/kernel/sound/core/oss/mixer_oss.c
+++ b/kernel/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
@@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
char line[128], str[32], idxstr[16];
const char *cptr;
- int ch, idx;
+ unsigned int idx;
+ int ch;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
@@ -1255,10 +1256,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/kernel/sound/core/oss/pcm_oss.c b/kernel/sound/core/oss/pcm_oss.c
index 58550cc93..33e72c809 100644
--- a/kernel/sound/core/oss/pcm_oss.c
+++ b/kernel/sound/core/oss/pcm_oss.c
@@ -834,7 +834,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
}
-static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
+static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
+ bool trylock)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_params *params, *sparams;
@@ -848,7 +849,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
struct snd_mask sformat_mask;
struct snd_mask mask;
- if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ if (trylock) {
+ if (!(mutex_trylock(&runtime->oss.params_lock)))
+ return -EAGAIN;
+ } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -EINTR;
sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
params = kmalloc(sizeof(*params), GFP_KERNEL);
@@ -1092,7 +1096,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
if (asubstream == NULL)
asubstream = substream;
if (substream->runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
if (err < 0)
return err;
}
@@ -1132,7 +1136,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
return 0;
runtime = substream->runtime;
if (runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
if (err < 0)
return err;
}
@@ -2163,7 +2167,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
runtime = substream->runtime;
if (runtime->oss.params &&
- (err = snd_pcm_oss_change_params(substream)) < 0)
+ (err = snd_pcm_oss_change_params(substream, false)) < 0)
return err;
info.fragsize = runtime->oss.period_bytes;
@@ -2800,7 +2804,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
return -EIO;
if (runtime->oss.params) {
- if ((err = snd_pcm_oss_change_params(substream)) < 0)
+ /* use mutex_trylock() for params_lock for avoiding a deadlock
+ * between mmap_sem and params_lock taken by
+ * copy_from/to_user() in snd_pcm_oss_write/read()
+ */
+ err = snd_pcm_oss_change_params(substream, true);
+ if (err < 0)
return err;
}
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
diff --git a/kernel/sound/core/pcm.c b/kernel/sound/core/pcm.c
index dfed728d8..308c9ecf7 100644
--- a/kernel/sound/core/pcm.c
+++ b/kernel/sound/core/pcm.c
@@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
snd_free_pages((void*)runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- kfree(runtime->hwptr_log);
-#endif
kfree(runtime);
substream->runtime = NULL;
put_pid(substream->pid);
@@ -1181,7 +1178,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 +1224,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/kernel/sound/core/pcm_compat.c b/kernel/sound/core/pcm_compat.c
index b48b43444..1f64ab0c2 100644
--- a/kernel/sound/core/pcm_compat.c
+++ b/kernel/sound/core/pcm_compat.c
@@ -183,6 +183,14 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream
return err;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
+static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_channel_info __user *src);
+#define snd_pcm_ioctl_channel_info_x32(s, p) \
+ snd_pcm_channel_info_user(s, p)
+#endif /* CONFIG_X86_X32 */
+
struct snd_pcm_status32 {
s32 state;
struct compat_timespec trigger_tstamp;
@@ -243,6 +251,71 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
return err;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has 64bit timespec and 64bit alignment */
+struct snd_pcm_status_x32 {
+ s32 state;
+ u32 rsvd; /* alignment */
+ struct timespec trigger_tstamp;
+ struct timespec tstamp;
+ u32 appl_ptr;
+ u32 hw_ptr;
+ s32 delay;
+ u32 avail;
+ u32 avail_max;
+ u32 overrange;
+ s32 suspended_state;
+ u32 audio_tstamp_data;
+ struct timespec audio_tstamp;
+ struct timespec driver_tstamp;
+ u32 audio_tstamp_accuracy;
+ unsigned char reserved[52-2*sizeof(struct timespec)];
+} __packed;
+
+#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
+
+static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
+ struct snd_pcm_status_x32 __user *src,
+ bool ext)
+{
+ struct snd_pcm_status status;
+ int err;
+
+ memset(&status, 0, sizeof(status));
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status.audio_tstamp_data,
+ (u32 __user *)(&src->audio_tstamp_data)))
+ return -EFAULT;
+ err = snd_pcm_status(substream, &status);
+ if (err < 0)
+ return err;
+
+ if (clear_user(src, sizeof(*src)))
+ return -EFAULT;
+ if (put_user(status.state, &src->state) ||
+ put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
+ put_timespec(&status.tstamp, &src->tstamp) ||
+ put_user(status.appl_ptr, &src->appl_ptr) ||
+ put_user(status.hw_ptr, &src->hw_ptr) ||
+ put_user(status.delay, &src->delay) ||
+ put_user(status.avail, &src->avail) ||
+ put_user(status.avail_max, &src->avail_max) ||
+ put_user(status.overrange, &src->overrange) ||
+ put_user(status.suspended_state, &src->suspended_state) ||
+ put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
+ put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
+ put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
+ put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
+ return -EFAULT;
+
+ return err;
+}
+#endif /* CONFIG_X86_X32 */
+
/* both for HW_PARAMS and HW_REFINE */
static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
int refine,
@@ -255,10 +328,15 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
if (! (runtime = substream->runtime))
return -ENOTTY;
- /* only fifo_size is different, so just copy all */
- data = memdup_user(data32, sizeof(*data32));
- if (IS_ERR(data))
- return PTR_ERR(data);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* only fifo_size (RO from userspace) is different, so just copy all */
+ if (copy_from_user(data, data32, sizeof(*data32))) {
+ err = -EFAULT;
+ goto error;
+ }
if (refine)
err = snd_pcm_hw_refine(substream, data);
@@ -464,6 +542,93 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
return 0;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has 64bit timespec and 64bit alignment */
+struct snd_pcm_mmap_status_x32 {
+ s32 state;
+ s32 pad1;
+ u32 hw_ptr;
+ u32 pad2; /* alignment */
+ struct timespec tstamp;
+ s32 suspended_state;
+ struct timespec audio_tstamp;
+} __packed;
+
+struct snd_pcm_mmap_control_x32 {
+ u32 appl_ptr;
+ u32 avail_min;
+};
+
+struct snd_pcm_sync_ptr_x32 {
+ u32 flags;
+ u32 rsvd; /* alignment */
+ union {
+ struct snd_pcm_mmap_status_x32 status;
+ unsigned char reserved[64];
+ } s;
+ union {
+ struct snd_pcm_mmap_control_x32 control;
+ unsigned char reserved[64];
+ } c;
+} __packed;
+
+static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr_x32 __user *src)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ u32 sflags;
+ struct snd_pcm_mmap_control scontrol;
+ struct snd_pcm_mmap_status sstatus;
+ snd_pcm_uframes_t boundary;
+ int err;
+
+ if (snd_BUG_ON(!runtime))
+ return -EINVAL;
+
+ if (get_user(sflags, &src->flags) ||
+ get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+ get_user(scontrol.avail_min, &src->c.control.avail_min))
+ return -EFAULT;
+ if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ status = runtime->status;
+ control = runtime->control;
+ boundary = recalculate_boundary(runtime);
+ if (!boundary)
+ boundary = 0x7fffffff;
+ snd_pcm_stream_lock_irq(substream);
+ /* FIXME: we should consider the boundary for the sync from app */
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ control->appl_ptr = scontrol.appl_ptr;
+ else
+ scontrol.appl_ptr = control->appl_ptr % boundary;
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = scontrol.avail_min;
+ else
+ scontrol.avail_min = control->avail_min;
+ sstatus.state = status->state;
+ sstatus.hw_ptr = status->hw_ptr % boundary;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
+ snd_pcm_stream_unlock_irq(substream);
+ if (put_user(sstatus.state, &src->s.status.state) ||
+ put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
+ put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
+ put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+ put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
+ put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+ put_user(scontrol.avail_min, &src->c.control.avail_min))
+ return -EFAULT;
+
+ return 0;
+}
+#endif /* CONFIG_X86_X32 */
/*
*/
@@ -482,7 +647,12 @@ enum {
SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
-
+#ifdef CONFIG_X86_X32
+ SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
+ SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
+ SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
+ SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
+#endif /* CONFIG_X86_X32 */
};
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -554,6 +724,16 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
return snd_pcm_ioctl_rewind_compat(substream, argp);
case SNDRV_PCM_IOCTL_FORWARD32:
return snd_pcm_ioctl_forward_compat(substream, argp);
+#ifdef CONFIG_X86_X32
+ case SNDRV_PCM_IOCTL_STATUS_X32:
+ return snd_pcm_status_user_x32(substream, argp, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
+ return snd_pcm_status_user_x32(substream, argp, true);
+ case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
+ return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
+ case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
+ return snd_pcm_ioctl_channel_info_x32(substream, argp);
+#endif /* CONFIG_X86_X32 */
}
return -ENOIOCTLCMD;
diff --git a/kernel/sound/core/pcm_drm_eld.c b/kernel/sound/core/pcm_drm_eld.c
new file mode 100644
index 000000000..e70379fb6
--- /dev/null
+++ b/kernel/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/kernel/sound/core/pcm_iec958.c b/kernel/sound/core/pcm_iec958.c
new file mode 100644
index 000000000..36b2d7aca
--- /dev/null
+++ b/kernel/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/kernel/sound/core/pcm_lib.c b/kernel/sound/core/pcm_lib.c
index 7d45645f1..6b5a811e0 100644
--- a/kernel/sound/core/pcm_lib.c
+++ b/kernel/sound/core/pcm_lib.c
@@ -801,7 +801,7 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
* negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratnum *rats,
+ unsigned int rats_count, const struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_den;
@@ -920,7 +920,8 @@ EXPORT_SYMBOL(snd_interval_ratnum);
* negative error code.
*/
static int snd_interval_ratden(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratden *rats,
+ unsigned int rats_count,
+ const struct snd_ratden *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_diff, best_den;
@@ -1339,7 +1340,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratnums *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratnums *r = rule->private;
unsigned int num = 0, den = 0;
int err;
err = snd_interval_ratnum(hw_param_interval(params, rule->var),
@@ -1363,10 +1364,10 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratnums *r)
+ const struct snd_pcm_hw_constraint_ratnums *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratnums, r,
+ snd_pcm_hw_rule_ratnums, (void *)r,
var, -1);
}
@@ -1375,7 +1376,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratdens *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratdens *r = rule->private;
unsigned int num = 0, den = 0;
int err = snd_interval_ratden(hw_param_interval(params, rule->var),
r->nrats, r->rats, &num, &den);
@@ -1398,10 +1399,10 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratdens *r)
+ const struct snd_pcm_hw_constraint_ratdens *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratdens, r,
+ snd_pcm_hw_rule_ratdens, (void *)r,
var, -1);
}
@@ -1875,20 +1876,17 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
return;
runtime = substream->runtime;
- if (runtime->transfer_ack_begin)
- runtime->transfer_ack_begin(substream);
-
snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
+#ifdef CONFIG_SND_PCM_TIMER
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
+#endif
_end:
snd_pcm_stream_unlock_irqrestore(substream, flags);
- if (runtime->transfer_ack_end)
- runtime->transfer_ack_end(substream);
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
}
diff --git a/kernel/sound/core/pcm_native.c b/kernel/sound/core/pcm_native.c
index ec8486c45..34e501868 100644
--- a/kernel/sound/core/pcm_native.c
+++ b/kernel/sound/core/pcm_native.c
@@ -74,6 +74,18 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem);
+/* Writer in rwsem may block readers even during its waiting in queue,
+ * and this may lead to a deadlock when the code path takes read sem
+ * twice (e.g. one in snd_pcm_action_nonatomic() and another in
+ * snd_pcm_stream_lock()). As a (suboptimal) workaround, let writer to
+ * spin until it gets the lock.
+ */
+static inline void down_write_nonblock(struct rw_semaphore *lock)
+{
+ while (!down_write_trylock(lock))
+ cond_resched();
+}
+
/**
* snd_pcm_stream_lock - Lock the PCM stream
* @substream: PCM substream
@@ -486,6 +498,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
snd_pcm_stream_unlock_irq(substream);
}
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+ int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+ if (substream->timer)
+ snd_timer_notify(substream->timer, event,
+ &substream->runtime->trigger_tstamp);
+#endif
+}
+
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -650,7 +672,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
}
snd_pcm_stream_unlock_irq(substream);
- if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ if (params->tstamp_mode < 0 ||
+ params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
return -EINVAL;
if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
@@ -1042,9 +1065,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}
static struct action_ops snd_pcm_action_start = {
@@ -1092,9 +1113,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
if (runtime->status->state != state) {
snd_pcm_trigger_tstamp(substream);
runtime->status->state = state;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
@@ -1208,18 +1227,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
snd_pcm_trigger_tstamp(substream);
if (push) {
runtime->status->state = SNDRV_PCM_STATE_PAUSED;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MPAUSE,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
} else {
runtime->status->state = SNDRV_PCM_STATE_RUNNING;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MCONTINUE,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
}
}
@@ -1267,9 +1280,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
snd_pcm_trigger_tstamp(substream);
runtime->status->suspended_state = runtime->status->state;
runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
@@ -1373,9 +1384,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->status->state = runtime->status->suspended_state;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
}
static struct action_ops snd_pcm_action_resume = {
@@ -1816,7 +1825,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
res = -ENOMEM;
goto _nolock;
}
- down_write(&snd_pcm_link_rwsem);
+ down_write_nonblock(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock);
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
substream->runtime->status->state != substream1->runtime->status->state ||
@@ -1863,7 +1872,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
struct snd_pcm_substream *s;
int res = 0;
- down_write(&snd_pcm_link_rwsem);
+ down_write_nonblock(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock);
if (!snd_pcm_stream_linked(substream)) {
res = -EALREADY;
@@ -2226,7 +2235,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream);
if (substream->hw_opened) {
- if (substream->ops->hw_free != NULL)
+ if (substream->ops->hw_free &&
+ substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
substream->ops->hw_free(substream);
substream->ops->close(substream);
substream->hw_opened = 0;
diff --git a/kernel/sound/core/rawmidi.c b/kernel/sound/core/rawmidi.c
index a7759846f..795437b10 100644
--- a/kernel/sound/core/rawmidi.c
+++ b/kernel/sound/core/rawmidi.c
@@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
+ spin_lock_irqsave(&runtime->lock, flags);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
count1 = count;
- spin_lock_irqsave(&runtime->lock, flags);
if (count1 > (int)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+ memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
if (copy_to_user(userbuf + result,
- runtime->buffer + runtime->appl_ptr, count1)) {
+ runtime->buffer + appl_ptr, count1)) {
return result > 0 ? result : -EFAULT;
}
spin_lock_irqsave(&runtime->lock, flags);
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
- spin_unlock_irqrestore(&runtime->lock, flags);
result += count1;
count -= count1;
}
+ spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
@@ -1055,23 +1060,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
/**
- * snd_rawmidi_transmit_peek - copy data from the internal buffer
+ * __snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
* @buffer: the buffer pointer
* @count: data size to transfer
*
- * Copies data from the internal output buffer to the given buffer.
- *
- * Call this in the interrupt handler when the midi output is ready,
- * and call snd_rawmidi_transmit_ack() after the transmission is
- * finished.
- *
- * Return: The size of copied data, or a negative error code on failure.
+ * This is a variant of snd_rawmidi_transmit_peek() without spinlock.
*/
-int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
- unsigned long flags;
int result, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -1081,7 +1079,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
return -EINVAL;
}
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
if (runtime->avail >= runtime->buffer_size) {
/* warning: lowlevel layer MUST trigger down the hardware */
goto __skip;
@@ -1106,25 +1103,47 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
}
}
__skip:
+ return result;
+}
+EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
+
+/**
+ * snd_rawmidi_transmit_peek - copy data from the internal buffer
+ * @substream: the rawmidi substream
+ * @buffer: the buffer pointer
+ * @count: data size to transfer
+ *
+ * Copies data from the internal output buffer to the given buffer.
+ *
+ * Call this in the interrupt handler when the midi output is ready,
+ * and call snd_rawmidi_transmit_ack() after the transmission is
+ * finished.
+ *
+ * Return: The size of copied data, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
+{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&runtime->lock, flags);
+ result = __snd_rawmidi_transmit_peek(substream, buffer, count);
spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
/**
- * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * __snd_rawmidi_transmit_ack - acknowledge the transmission
* @substream: the rawmidi substream
* @count: the transferred count
*
- * Advances the hardware pointer for the internal output buffer with
- * the given size and updates the condition.
- * Call after the transmission is finished.
- *
- * Return: The advanced size if successful, or a negative error code on failure.
+ * This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
*/
-int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{
- unsigned long flags;
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
@@ -1132,7 +1151,6 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
"snd_rawmidi_transmit_ack: output is not active!!!\n");
return -EINVAL;
}
- spin_lock_irqsave(&runtime->lock, flags);
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
@@ -1142,9 +1160,32 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
if (runtime->drain || snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
return count;
}
+EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
+
+/**
+ * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the transferred count
+ *
+ * Advances the hardware pointer for the internal output buffer with
+ * the given size and updates the condition.
+ * Call after the transmission is finished.
+ *
+ * Return: The advanced size if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&runtime->lock, flags);
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ return result;
+}
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
/**
@@ -1160,12 +1201,22 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&runtime->lock, flags);
if (!substream->opened)
- return -EBADFD;
- count = snd_rawmidi_transmit_peek(substream, buffer, count);
- if (count < 0)
- return count;
- return snd_rawmidi_transmit_ack(substream, count);
+ result = -EBADFD;
+ else {
+ count = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ if (count <= 0)
+ result = count;
+ else
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ }
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit);
@@ -1177,8 +1228,9 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long count1, result;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
- if (snd_BUG_ON(!kernelbuf && !userbuf))
+ if (!kernelbuf && !userbuf)
return -EINVAL;
if (snd_BUG_ON(!runtime->buffer))
return -EINVAL;
@@ -1197,12 +1249,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (long)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(runtime->buffer + runtime->appl_ptr,
+ memcpy(runtime->buffer + appl_ptr,
kernelbuf + result, count1);
else if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
- if (copy_from_user(runtime->buffer + runtime->appl_ptr,
+ if (copy_from_user(runtime->buffer + appl_ptr,
userbuf + result, count1)) {
spin_lock_irqsave(&runtime->lock, flags);
result = result > 0 ? result : -EFAULT;
@@ -1210,9 +1269,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
}
spin_lock_irqsave(&runtime->lock, flags);
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
result += count1;
count -= count1;
}
diff --git a/kernel/sound/core/rawmidi_compat.c b/kernel/sound/core/rawmidi_compat.c
index 5268c1f58..09a89094d 100644
--- a/kernel/sound/core/rawmidi_compat.c
+++ b/kernel/sound/core/rawmidi_compat.c
@@ -94,9 +94,58 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
return 0;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has 64bit timespec and 64bit alignment */
+struct snd_rawmidi_status_x32 {
+ s32 stream;
+ u32 rsvd; /* alignment */
+ struct timespec tstamp;
+ u32 avail;
+ u32 xruns;
+ unsigned char reserved[16];
+} __attribute__((packed));
+
+#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
+
+static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile,
+ struct snd_rawmidi_status_x32 __user *src)
+{
+ int err;
+ struct snd_rawmidi_status status;
+
+ if (rfile->output == NULL)
+ return -EINVAL;
+ if (get_user(status.stream, &src->stream))
+ return -EFAULT;
+
+ switch (status.stream) {
+ case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ err = snd_rawmidi_output_status(rfile->output, &status);
+ break;
+ case SNDRV_RAWMIDI_STREAM_INPUT:
+ err = snd_rawmidi_input_status(rfile->input, &status);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+
+ if (put_timespec(&status.tstamp, &src->tstamp) ||
+ put_user(status.avail, &src->avail) ||
+ put_user(status.xruns, &src->xruns))
+ return -EFAULT;
+
+ return 0;
+}
+#endif /* CONFIG_X86_X32 */
+
enum {
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
+#ifdef CONFIG_X86_X32
+ SNDRV_RAWMIDI_IOCTL_STATUS_X32 = _IOWR('W', 0x20, struct snd_rawmidi_status_x32),
+#endif /* CONFIG_X86_X32 */
};
static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -115,6 +164,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
return snd_rawmidi_ioctl_params_compat(rfile, argp);
case SNDRV_RAWMIDI_IOCTL_STATUS32:
return snd_rawmidi_ioctl_status_compat(rfile, argp);
+#ifdef CONFIG_X86_X32
+ case SNDRV_RAWMIDI_IOCTL_STATUS_X32:
+ return snd_rawmidi_ioctl_status_x32(rfile, argp);
+#endif /* CONFIG_X86_X32 */
}
return -ENOIOCTLCMD;
}
diff --git a/kernel/sound/core/seq/Makefile b/kernel/sound/core/seq/Makefile
index 941f64a85..b65fa5a19 100644
--- a/kernel/sound/core/seq/Makefile
+++ b/kernel/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/kernel/sound/core/seq/oss/seq_oss.c b/kernel/sound/core/seq/oss/seq_oss.c
index 72873a46a..cb2389910 100644
--- a/kernel/sound/core/seq/oss/seq_oss.c
+++ b/kernel/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
@@ -148,8 +148,6 @@ odev_release(struct inode *inode, struct file *file)
if ((dp = file->private_data) == NULL)
return 0;
- snd_seq_oss_drain_write(dp);
-
mutex_lock(&register_mutex);
snd_seq_oss_release(dp);
mutex_unlock(&register_mutex);
@@ -261,7 +259,7 @@ unregister_device(void)
* /proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
@@ -303,4 +301,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/kernel/sound/core/seq/oss/seq_oss_device.h b/kernel/sound/core/seq/oss/seq_oss_device.h
index b43924325..d7b4d016b 100644
--- a/kernel/sound/core/seq/oss/seq_oss_device.h
+++ b/kernel/sound/core/seq/oss/seq_oss_device.h
@@ -127,7 +127,6 @@ int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int co
unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
-void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp);
/* */
void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
diff --git a/kernel/sound/core/seq/oss/seq_oss_init.c b/kernel/sound/core/seq/oss/seq_oss_init.c
index 2de3feff7..92c96a95a 100644
--- a/kernel/sound/core/seq/oss/seq_oss_init.c
+++ b/kernel/sound/core/seq/oss/seq_oss_init.c
@@ -202,7 +202,7 @@ snd_seq_oss_open(struct file *file, int level)
dp->index = i;
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
- pr_err("ALSA: seq_oss: too many applications\n");
+ pr_debug("ALSA: seq_oss: too many applications\n");
rc = -ENOMEM;
goto _error;
}
@@ -436,22 +436,6 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp)
/*
- * Wait until the queue is empty (if we don't have nonblock)
- */
-void
-snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
-{
- if (! dp->timer->running)
- return;
- if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
- dp->writeq) {
- while (snd_seq_oss_writeq_sync(dp->writeq))
- ;
- }
-}
-
-
-/*
* reset sequencer devices
*/
void
@@ -479,8 +463,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 +514,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/kernel/sound/core/seq/oss/seq_oss_midi.c b/kernel/sound/core/seq/oss/seq_oss_midi.c
index 96e8395ae..aaff9ee32 100644
--- a/kernel/sound/core/seq/oss/seq_oss_midi.c
+++ b/kernel/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/kernel/sound/core/seq/oss/seq_oss_readq.c b/kernel/sound/core/seq/oss/seq_oss_readq.c
index c080c73ce..046cb586f 100644
--- a/kernel/sound/core/seq/oss/seq_oss_readq.c
+++ b/kernel/sound/core/seq/oss/seq_oss_readq.c
@@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
q->head = q->tail = 0;
}
/* if someone sleeping, wake'em up */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
@@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
q->qlen++;
/* wake up sleeper */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
spin_unlock_irqrestore(&q->lock, flags);
@@ -222,7 +220,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 +231,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/kernel/sound/core/seq/oss/seq_oss_synth.c b/kernel/sound/core/seq/oss/seq_oss_synth.c
index 48e4fe1b6..b16dbef04 100644
--- a/kernel/sound/core/seq/oss/seq_oss_synth.c
+++ b/kernel/sound/core/seq/oss/seq_oss_synth.c
@@ -308,7 +308,7 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+ if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
return;
for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i];
@@ -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/kernel/sound/core/seq/oss/seq_oss_writeq.c b/kernel/sound/core/seq/oss/seq_oss_writeq.c
index d50338bbc..1f6788a18 100644
--- a/kernel/sound/core/seq/oss/seq_oss_writeq.c
+++ b/kernel/sound/core/seq/oss/seq_oss_writeq.c
@@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
spin_lock_irqsave(&q->sync_lock, flags);
q->sync_time = time;
q->sync_event_put = 0;
- if (waitqueue_active(&q->sync_sleep)) {
- wake_up(&q->sync_sleep);
- }
+ wake_up(&q->sync_sleep);
spin_unlock_irqrestore(&q->sync_lock, flags);
}
diff --git a/kernel/sound/core/seq/seq_clientmgr.c b/kernel/sound/core/seq/seq_clientmgr.c
index edbdab85f..58e79e02f 100644
--- a/kernel/sound/core/seq/seq_clientmgr.c
+++ b/kernel/sound/core/seq/seq_clientmgr.c
@@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
else
down_read(&grp->list_mutex);
list_for_each_entry(subs, &grp->list_head, src_list) {
+ /* both ports ready? */
+ if (atomic_read(&subs->ref_count) != 2)
+ continue;
event->dest = subs->info.dest;
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
/* convert time according to flag with subscription */
@@ -1962,7 +1965,7 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
* No restrictions so for a user client we can clear
* the whole fifo
*/
- if (client->type == USER_CLIENT)
+ if (client->type == USER_CLIENT && client->data.user.fifo)
snd_seq_fifo_clear(client->data.user.fifo);
}
@@ -2447,7 +2450,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
/*---------------------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* /proc interface
*/
@@ -2549,7 +2552,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/kernel/sound/core/seq/seq_compat.c b/kernel/sound/core/seq/seq_compat.c
index 81f7c109d..65175902a 100644
--- a/kernel/sound/core/seq/seq_compat.c
+++ b/kernel/sound/core/seq/seq_compat.c
@@ -49,11 +49,12 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
struct snd_seq_port_info *data;
mm_segment_t fs;
- data = memdup_user(data32, sizeof(*data32));
- if (IS_ERR(data))
- return PTR_ERR(data);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- if (get_user(data->flags, &data32->flags) ||
+ if (copy_from_user(data, data32, sizeof(*data32)) ||
+ get_user(data->flags, &data32->flags) ||
get_user(data->time_queue, &data32->time_queue))
goto error;
data->kernel = NULL;
diff --git a/kernel/sound/core/seq/seq_device.c b/kernel/sound/core/seq/seq_device.c
index d99f99d61..c4acf17e9 100644
--- a/kernel/sound/core/seq/seq_device.c
+++ b/kernel/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/kernel/sound/core/seq/seq_info.c b/kernel/sound/core/seq/seq_info.c
index acf776941..97015447b 100644
--- a/kernel/sound/core/seq/seq_info.c
+++ b/kernel/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/kernel/sound/core/seq/seq_info.h b/kernel/sound/core/seq/seq_info.h
index 4892a7f35..f8549f81a 100644
--- a/kernel/sound/core/seq/seq_info.h
+++ b/kernel/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/kernel/sound/core/seq/seq_memory.c b/kernel/sound/core/seq/seq_memory.c
index 801076687..c850345c4 100644
--- a/kernel/sound/core/seq/seq_memory.c
+++ b/kernel/sound/core/seq/seq_memory.c
@@ -383,15 +383,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
if (snd_BUG_ON(!pool))
return -EINVAL;
- if (pool->ptr) /* should be atomic? */
- return 0;
- pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
- if (!pool->ptr)
+ cellptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
+ if (!cellptr)
return -ENOMEM;
/* add new cells to the free cell list */
spin_lock_irqsave(&pool->lock, flags);
+ if (pool->ptr) {
+ spin_unlock_irqrestore(&pool->lock, flags);
+ vfree(cellptr);
+ return 0;
+ }
+
+ pool->ptr = cellptr;
pool->free = NULL;
for (cell = 0; cell < pool->size; cell++) {
diff --git a/kernel/sound/core/seq/seq_ports.c b/kernel/sound/core/seq/seq_ports.c
index 55170a20a..fe686ee41 100644
--- a/kernel/sound/core/seq/seq_ports.c
+++ b/kernel/sound/core/seq/seq_ports.c
@@ -173,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
}
/* */
-enum group_type {
- SRC_LIST, DEST_LIST
-};
-
static int subscribe_port(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
@@ -203,6 +199,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
return NULL;
}
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack);
+
+static inline struct snd_seq_subscribers *
+get_subscriber(struct list_head *p, bool is_src)
+{
+ if (is_src)
+ return list_entry(p, struct snd_seq_subscribers, src_list);
+ else
+ return list_entry(p, struct snd_seq_subscribers, dest_list);
+}
+
/*
* remove all subscribers on the list
* this is called from port_delete, for each src and dest list.
@@ -210,7 +220,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
- int grptype)
+ int is_src)
{
struct list_head *p, *n;
@@ -219,15 +229,13 @@ static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client *c;
struct snd_seq_client_port *aport;
- if (grptype == SRC_LIST) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ subs = get_subscriber(p, is_src);
+ if (is_src)
aport = get_client_port(&subs->info.dest, &c);
- } else {
- subs = list_entry(p, struct snd_seq_subscribers, dest_list);
+ else
aport = get_client_port(&subs->info.sender, &c);
- }
- list_del(p);
- unsubscribe_port(client, port, grp, &subs->info, 0);
+ delete_and_unsubscribe_port(client, port, subs, is_src, false);
+
if (!aport) {
/* looks like the connected port is being deleted.
* we decrease the counter, and when both ports are deleted
@@ -235,21 +243,14 @@ static void clear_subscriber_list(struct snd_seq_client *client,
*/
if (atomic_dec_and_test(&subs->ref_count))
kfree(subs);
- } else {
- /* ok we got the connected port */
- struct snd_seq_port_subs_info *agrp;
- agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
- down_write(&agrp->list_mutex);
- if (grptype == SRC_LIST)
- list_del(&subs->dest_list);
- else
- list_del(&subs->src_list);
- up_write(&agrp->list_mutex);
- unsubscribe_port(c, aport, agrp, &subs->info, 1);
- kfree(subs);
- snd_seq_port_unlock(aport);
- snd_seq_client_unlock(c);
+ continue;
}
+
+ /* ok we got the connected port */
+ delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
+ kfree(subs);
+ snd_seq_port_unlock(aport);
+ snd_seq_client_unlock(c);
}
}
@@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client,
snd_use_lock_sync(&port->use_lock);
/* clear subscribers info */
- clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
- clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+ clear_subscriber_list(client, port, &port->c_src, true);
+ clear_subscriber_list(client, port, &port->c_dest, false);
if (port->private_free)
port->private_free(port->private_data);
@@ -479,85 +480,123 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
return 0;
}
-
-/* connect two ports */
-int snd_seq_port_connect(struct snd_seq_client *connector,
- struct snd_seq_client *src_client,
- struct snd_seq_client_port *src_port,
- struct snd_seq_client *dest_client,
- struct snd_seq_client_port *dest_port,
- struct snd_seq_port_subscribe *info)
+static int check_and_subscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool exclusive, bool ack)
{
- struct snd_seq_port_subs_info *src = &src_port->c_src;
- struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
- struct snd_seq_subscribers *subs, *s;
- int err, src_called = 0;
- unsigned long flags;
- int exclusive;
-
- subs = kzalloc(sizeof(*subs), GFP_KERNEL);
- if (! subs)
- return -ENOMEM;
-
- subs->info = *info;
- atomic_set(&subs->ref_count, 2);
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *p;
+ struct snd_seq_subscribers *s;
+ int err;
- down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
-
- exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
+ grp = is_src ? &port->c_src : &port->c_dest;
err = -EBUSY;
+ down_write(&grp->list_mutex);
if (exclusive) {
- if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
+ if (!list_empty(&grp->list_head))
goto __error;
} else {
- if (src->exclusive || dest->exclusive)
+ if (grp->exclusive)
goto __error;
/* check whether already exists */
- list_for_each_entry(s, &src->list_head, src_list) {
- if (match_subs_info(info, &s->info))
- goto __error;
- }
- list_for_each_entry(s, &dest->list_head, dest_list) {
- if (match_subs_info(info, &s->info))
+ list_for_each(p, &grp->list_head) {
+ s = get_subscriber(p, is_src);
+ if (match_subs_info(&subs->info, &s->info))
goto __error;
}
}
- if ((err = subscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number)) < 0)
- goto __error;
- src_called = 1;
-
- if ((err = subscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number)) < 0)
+ err = subscribe_port(client, port, grp, &subs->info, ack);
+ if (err < 0) {
+ grp->exclusive = 0;
goto __error;
+ }
/* add to list */
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no other lock yet
- list_add_tail(&subs->src_list, &src->list_head);
- list_add_tail(&subs->dest_list, &dest->list_head);
- // write_unlock(&dest->list_lock); // no other lock yet
- write_unlock_irqrestore(&src->list_lock, flags);
+ write_lock_irq(&grp->list_lock);
+ if (is_src)
+ list_add_tail(&subs->src_list, &grp->list_head);
+ else
+ list_add_tail(&subs->dest_list, &grp->list_head);
+ grp->exclusive = exclusive;
+ atomic_inc(&subs->ref_count);
+ write_unlock_irq(&grp->list_lock);
+ err = 0;
- src->exclusive = dest->exclusive = exclusive;
+ __error:
+ up_write(&grp->list_mutex);
+ return err;
+}
+
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *list;
+ bool empty;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ list = is_src ? &subs->src_list : &subs->dest_list;
+ down_write(&grp->list_mutex);
+ write_lock_irq(&grp->list_lock);
+ empty = list_empty(list);
+ if (!empty)
+ list_del_init(list);
+ grp->exclusive = 0;
+ write_unlock_irq(&grp->list_lock);
+ up_write(&grp->list_mutex);
+
+ if (!empty)
+ unsubscribe_port(client, port, grp, &subs->info, ack);
+}
+
+/* connect two ports */
+int snd_seq_port_connect(struct snd_seq_client *connector,
+ struct snd_seq_client *src_client,
+ struct snd_seq_client_port *src_port,
+ struct snd_seq_client *dest_client,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_port_subscribe *info)
+{
+ struct snd_seq_subscribers *subs;
+ bool exclusive;
+ int err;
+
+ subs = kzalloc(sizeof(*subs), GFP_KERNEL);
+ if (!subs)
+ return -ENOMEM;
+
+ subs->info = *info;
+ atomic_set(&subs->ref_count, 0);
+ INIT_LIST_HEAD(&subs->src_list);
+ INIT_LIST_HEAD(&subs->dest_list);
+
+ exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
+
+ err = check_and_subscribe_port(src_client, src_port, subs, true,
+ exclusive,
+ connector->number != src_client->number);
+ if (err < 0)
+ goto error;
+ err = check_and_subscribe_port(dest_client, dest_port, subs, false,
+ exclusive,
+ connector->number != dest_client->number);
+ if (err < 0)
+ goto error_dest;
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return 0;
- __error:
- if (src_called)
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
+ error_dest:
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ error:
kfree(subs);
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return err;
}
-
/* remove the connection */
int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_client *src_client,
@@ -567,37 +606,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_port_subscribe *info)
{
struct snd_seq_port_subs_info *src = &src_port->c_src;
- struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
struct snd_seq_subscribers *subs;
int err = -ENOENT;
- unsigned long flags;
down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
-
/* look for the connection */
list_for_each_entry(subs, &src->list_head, src_list) {
if (match_subs_info(info, &subs->info)) {
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no lock yet
- list_del(&subs->src_list);
- list_del(&subs->dest_list);
- // write_unlock(&dest->list_lock);
- write_unlock_irqrestore(&src->list_lock, flags);
- src->exclusive = dest->exclusive = 0;
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
- unsubscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number);
- kfree(subs);
+ atomic_dec(&subs->ref_count); /* mark as not ready */
err = 0;
break;
}
}
-
- up_write(&dest->list_mutex);
up_write(&src->list_mutex);
- return err;
+ if (err < 0)
+ return err;
+
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
+ connector->number != dest_client->number);
+ kfree(subs);
+ return 0;
}
diff --git a/kernel/sound/core/seq/seq_queue.c b/kernel/sound/core/seq/seq_queue.c
index a0cda3820..0bec02e89 100644
--- a/kernel/sound/core/seq/seq_queue.c
+++ b/kernel/sound/core/seq/seq_queue.c
@@ -142,8 +142,10 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
static void queue_delete(struct snd_seq_queue *q)
{
/* stop and release the timer */
+ mutex_lock(&q->timer_mutex);
snd_seq_timer_stop(q->timer);
snd_seq_timer_close(q);
+ mutex_unlock(&q->timer_mutex);
/* wait until access free */
snd_use_lock_sync(&q->use_lock);
/* release resources... */
@@ -753,7 +755,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 +789,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/kernel/sound/core/seq/seq_timer.c b/kernel/sound/core/seq/seq_timer.c
index 186f16111..293104926 100644
--- a/kernel/sound/core/seq/seq_timer.c
+++ b/kernel/sound/core/seq/seq_timer.c
@@ -90,6 +90,9 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmr->lock, flags);
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
@@ -105,21 +108,25 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
tmr->preferred_resolution = seq_default_timer_resolution;
tmr->skew = tmr->skew_base = SKEW_BASE;
+ spin_unlock_irqrestore(&tmr->lock, flags);
}
-void snd_seq_timer_reset(struct snd_seq_timer * tmr)
+static void seq_timer_reset(struct snd_seq_timer *tmr)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tmr->lock, flags);
-
/* reset time & songposition */
tmr->cur_time.tv_sec = 0;
tmr->cur_time.tv_nsec = 0;
tmr->tick.cur_tick = 0;
tmr->tick.fraction = 0;
+}
+
+void snd_seq_timer_reset(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tmr->lock, flags);
+ seq_timer_reset(tmr);
spin_unlock_irqrestore(&tmr->lock, flags);
}
@@ -138,8 +145,11 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
tmr = q->timer;
if (tmr == NULL)
return;
- if (!tmr->running)
+ spin_lock_irqsave(&tmr->lock, flags);
+ if (!tmr->running) {
+ spin_unlock_irqrestore(&tmr->lock, flags);
return;
+ }
resolution *= ticks;
if (tmr->skew != tmr->skew_base) {
@@ -148,8 +158,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
(((resolution & 0xffff) * tmr->skew) >> 16);
}
- spin_lock_irqsave(&tmr->lock, flags);
-
/* update timer */
snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
@@ -296,26 +304,30 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
t->callback = snd_seq_timer_interrupt;
t->callback_data = q;
t->flags |= SNDRV_TIMER_IFLG_AUTO;
+ spin_lock_irq(&tmr->lock);
tmr->timeri = t;
+ spin_unlock_irq(&tmr->lock);
return 0;
}
int snd_seq_timer_close(struct snd_seq_queue *q)
{
struct snd_seq_timer *tmr;
+ struct snd_timer_instance *t;
tmr = q->timer;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- if (tmr->timeri) {
- snd_timer_stop(tmr->timeri);
- snd_timer_close(tmr->timeri);
- tmr->timeri = NULL;
- }
+ spin_lock_irq(&tmr->lock);
+ t = tmr->timeri;
+ tmr->timeri = NULL;
+ spin_unlock_irq(&tmr->lock);
+ if (t)
+ snd_timer_close(t);
return 0;
}
-int snd_seq_timer_stop(struct snd_seq_timer * tmr)
+static int seq_timer_stop(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
@@ -326,6 +338,17 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr)
return 0;
}
+int snd_seq_timer_stop(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_stop(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
static int initialize_timer(struct snd_seq_timer *tmr)
{
struct snd_timer *t;
@@ -358,13 +381,13 @@ static int initialize_timer(struct snd_seq_timer *tmr)
return 0;
}
-int snd_seq_timer_start(struct snd_seq_timer * tmr)
+static int seq_timer_start(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
- snd_seq_timer_stop(tmr);
- snd_seq_timer_reset(tmr);
+ seq_timer_stop(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
snd_timer_start(tmr->timeri, tmr->ticks);
@@ -373,14 +396,25 @@ int snd_seq_timer_start(struct snd_seq_timer * tmr)
return 0;
}
-int snd_seq_timer_continue(struct snd_seq_timer * tmr)
+int snd_seq_timer_start(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_start(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
+static int seq_timer_continue(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
return -EBUSY;
if (! tmr->initialized) {
- snd_seq_timer_reset(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
}
@@ -390,11 +424,24 @@ int snd_seq_timer_continue(struct snd_seq_timer * tmr)
return 0;
}
+int snd_seq_timer_continue(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_continue(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
/* return current 'real' time. use timeofday() to get better granularity. */
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
{
snd_seq_real_time_t cur_time;
+ unsigned long flags;
+ spin_lock_irqsave(&tmr->lock, flags);
cur_time = tmr->cur_time;
if (tmr->running) {
struct timeval tm;
@@ -410,7 +457,7 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
}
snd_seq_sanity_real_time(&cur_time);
}
-
+ spin_unlock_irqrestore(&tmr->lock, flags);
return cur_time;
}
@@ -422,7 +469,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 +496,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/kernel/sound/core/seq/seq_virmidi.c b/kernel/sound/core/seq/seq_virmidi.c
index 56e0f4cd3..81134e067 100644
--- a/kernel/sound/core/seq/seq_virmidi.c
+++ b/kernel/sound/core/seq/seq_virmidi.c
@@ -155,21 +155,26 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
struct snd_virmidi *vmidi = substream->runtime->private_data;
int count, res;
unsigned char buf[32], *pbuf;
+ unsigned long flags;
if (up) {
vmidi->trigger = 1;
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
- snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
- return; /* ignored */
+ while (snd_rawmidi_transmit(substream, buf,
+ sizeof(buf)) > 0) {
+ /* ignored */
+ }
+ return;
}
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
return;
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
}
+ spin_lock_irqsave(&substream->runtime->lock, flags);
while (1) {
- count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
+ count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
if (count <= 0)
break;
pbuf = buf;
@@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
snd_midi_event_reset_encode(vmidi->parser);
continue;
}
- snd_rawmidi_transmit_ack(substream, res);
+ __snd_rawmidi_transmit_ack(substream, res);
pbuf += res;
count -= res;
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
+ goto out;
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
}
}
}
+ out:
+ spin_unlock_irqrestore(&substream->runtime->lock, flags);
} else {
vmidi->trigger = 0;
}
@@ -254,9 +261,13 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
*/
static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
{
+ struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_virmidi *vmidi = substream->runtime->private_data;
- snd_midi_event_free(vmidi->parser);
+
+ write_lock_irq(&rdev->filelist_lock);
list_del(&vmidi->list);
+ write_unlock_irq(&rdev->filelist_lock);
+ snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
return 0;
diff --git a/kernel/sound/core/sound.c b/kernel/sound/core/sound.c
index 5fc93d005..175f9e4e0 100644
--- a/kernel/sound/core/sound.c
+++ b/kernel/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/kernel/sound/core/sound_oss.c b/kernel/sound/core/sound_oss.c
index 573a65eb2..0ca9d72b2 100644
--- a/kernel/sound/core/sound_oss.c
+++ b/kernel/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/kernel/sound/core/timer.c b/kernel/sound/core/timer.c
index a9a1a047c..f24c9fccf 100644
--- a/kernel/sound/core/timer.c
+++ b/kernel/sound/core/timer.c
@@ -65,6 +65,7 @@ struct snd_timer_user {
int qtail;
int qused;
int queue_size;
+ bool disconnected;
struct snd_timer_read *queue;
struct snd_timer_tread *tqueue;
spinlock_t qlock;
@@ -73,7 +74,7 @@ struct snd_timer_user {
struct timespec tstamp; /* trigger tstamp */
wait_queue_head_t qchange_sleep;
struct fasync_struct *fasync;
- struct mutex tread_sem;
+ struct mutex ioctl_lock;
};
/* list of timers */
@@ -215,11 +216,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
slave->slave_id == master->slave_id) {
list_move_tail(&slave->open_list, &master->slave_list_head);
spin_lock_irq(&slave_active_lock);
+ spin_lock(&master->timer->lock);
slave->master = master;
slave->timer = master->timer;
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
list_add_tail(&slave->active_list,
&master->slave_active_head);
+ spin_unlock(&master->timer->lock);
spin_unlock_irq(&slave_active_lock);
}
}
@@ -288,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
mutex_unlock(&register_mutex);
return -ENOMEM;
}
+ /* take a card refcount for safe disconnection */
+ if (timer->card)
+ get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open)
@@ -299,8 +305,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
return 0;
}
-static int _snd_timer_stop(struct snd_timer_instance *timeri,
- int keep_flag, int event);
+static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
/*
* close a timer instance
@@ -342,19 +347,25 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_unlock_irq(&timer->lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
- if (timer && list_empty(&timer->open_list_head) &&
+ if (list_empty(&timer->open_list_head) &&
timer->hw.close)
timer->hw.close(timer);
/* remove slave links */
+ spin_lock_irq(&slave_active_lock);
+ spin_lock(&timer->lock);
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
open_list) {
- spin_lock_irq(&slave_active_lock);
- _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
list_move_tail(&slave->open_list, &snd_timer_slave_list);
slave->master = NULL;
slave->timer = NULL;
- spin_unlock_irq(&slave_active_lock);
+ list_del_init(&slave->ack_list);
+ list_del_init(&slave->active_list);
}
+ spin_unlock(&timer->lock);
+ spin_unlock_irq(&slave_active_lock);
+ /* release a card refcount for safe disconnection */
+ if (timer->card)
+ put_device(&timer->card->card_dev);
mutex_unlock(&register_mutex);
}
out:
@@ -411,7 +422,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
spin_lock_irqsave(&timer->lock, flags);
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
- ts->ccallback(ti, event + 100, &tstamp, resolution);
+ ts->ccallback(ts, event + 100, &tstamp, resolution);
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -440,10 +451,17 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
unsigned long flags;
spin_lock_irqsave(&slave_active_lock, flags);
+ if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+ spin_unlock_irqrestore(&slave_active_lock, flags);
+ return -EBUSY;
+ }
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- if (timeri->master)
+ if (timeri->master && timeri->timer) {
+ spin_lock(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
+ spin_unlock(&timeri->timer->lock);
+ }
spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */
}
@@ -461,23 +479,32 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
return -EINVAL;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
result = snd_timer_start_slave(timeri);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+ if (result >= 0)
+ snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
return result;
}
timer = timeri->timer;
if (timer == NULL)
return -EINVAL;
+ if (timer->card && timer->card->shutdown)
+ return -ENODEV;
spin_lock_irqsave(&timer->lock, flags);
+ if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START)) {
+ result = -EBUSY;
+ goto unlock;
+ }
timeri->ticks = timeri->cticks = ticks;
timeri->pticks = 0;
result = snd_timer_start1(timer, timeri, ticks);
+ unlock:
spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+ if (result >= 0)
+ snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
return result;
}
-static int _snd_timer_stop(struct snd_timer_instance * timeri,
- int keep_flag, int event)
+static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
{
struct snd_timer *timer;
unsigned long flags;
@@ -486,19 +513,36 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
return -ENXIO;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- if (!keep_flag) {
- spin_lock_irqsave(&slave_active_lock, flags);
- timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ spin_lock_irqsave(&slave_active_lock, flags);
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
spin_unlock_irqrestore(&slave_active_lock, flags);
+ return -EBUSY;
}
+ if (timeri->timer)
+ spin_lock(&timeri->timer->lock);
+ timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ list_del_init(&timeri->ack_list);
+ list_del_init(&timeri->active_list);
+ if (timeri->timer)
+ spin_unlock(&timeri->timer->lock);
+ spin_unlock_irqrestore(&slave_active_lock, flags);
goto __end;
}
timer = timeri->timer;
if (!timer)
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
+ if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START))) {
+ spin_unlock_irqrestore(&timer->lock, flags);
+ return -EBUSY;
+ }
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
+ if (timer->card && timer->card->shutdown) {
+ spin_unlock_irqrestore(&timer->lock, flags);
+ return 0;
+ }
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
timer->hw.stop(timer);
@@ -511,9 +555,7 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
}
}
}
- if (!keep_flag)
- timeri->flags &=
- ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
spin_unlock_irqrestore(&timer->lock, flags);
__end:
if (event != SNDRV_TIMER_EVENT_RESOLUTION)
@@ -532,7 +574,7 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
unsigned long flags;
int err;
- err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);
+ err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
if (err < 0)
return err;
timer = timeri->timer;
@@ -561,11 +603,18 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
timer = timeri->timer;
if (! timer)
return -EINVAL;
+ if (timer->card && timer->card->shutdown)
+ return -ENODEV;
spin_lock_irqsave(&timer->lock, flags);
+ if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+ result = -EBUSY;
+ goto unlock;
+ }
if (!timeri->cticks)
timeri->cticks = 1;
timeri->pticks = 0;
result = snd_timer_start1(timer, timeri, timer->sticks);
+ unlock:
spin_unlock_irqrestore(&timer->lock, flags);
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
return result;
@@ -576,7 +625,7 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
*/
int snd_timer_pause(struct snd_timer_instance * timeri)
{
- return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);
+ return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
}
/*
@@ -624,6 +673,9 @@ static void snd_timer_tasklet(unsigned long arg)
unsigned long resolution, ticks;
unsigned long flags;
+ if (timer->card && timer->card->shutdown)
+ return;
+
spin_lock_irqsave(&timer->lock, flags);
/* now process all callbacks */
while (!list_empty(&timer->sack_list_head)) {
@@ -664,6 +716,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
if (timer == NULL)
return;
+ if (timer->card && timer->card->shutdown)
+ return;
+
spin_lock_irqsave(&timer->lock, flags);
/* remember the current resolution */
@@ -693,8 +748,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ti->cticks = ti->ticks;
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- if (--timer->running)
- list_del(&ti->active_list);
+ --timer->running;
+ list_del_init(&ti->active_list);
}
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
@@ -874,11 +929,28 @@ static int snd_timer_dev_register(struct snd_device *dev)
return 0;
}
+/* just for reference in snd_timer_dev_disconnect() below */
+static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
+ int event, struct timespec *tstamp,
+ unsigned long resolution);
+
static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct snd_timer *timer = device->device_data;
+ struct snd_timer_instance *ti;
+
mutex_lock(&register_mutex);
list_del_init(&timer->device_list);
+ /* wake up pending sleepers */
+ list_for_each_entry(ti, &timer->open_list_head, open_list) {
+ /* FIXME: better to have a ti.disconnect() op */
+ if (ti->ccallback == snd_timer_user_ccallback) {
+ struct snd_timer_user *tu = ti->callback_data;
+
+ tu->disconnected = true;
+ wake_up(&tu->qchange_sleep);
+ }
+ }
mutex_unlock(&register_mutex);
return 0;
}
@@ -889,6 +961,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
+ if (timer->card && timer->card->shutdown)
+ return;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
@@ -1034,7 +1108,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
*/
@@ -1047,6 +1121,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
+ if (timer->card && timer->card->shutdown)
+ continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1104,7 +1180,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
@@ -1253,7 +1329,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
return -ENOMEM;
spin_lock_init(&tu->qlock);
init_waitqueue_head(&tu->qchange_sleep);
- mutex_init(&tu->tread_sem);
+ mutex_init(&tu->ioctl_lock);
tu->ticks = 1;
tu->queue_size = 128;
tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
@@ -1273,8 +1349,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
if (file->private_data) {
tu = file->private_data;
file->private_data = NULL;
+ mutex_lock(&tu->ioctl_lock);
if (tu->timeri)
snd_timer_close(tu->timeri);
+ mutex_unlock(&tu->ioctl_lock);
kfree(tu->queue);
kfree(tu->tqueue);
kfree(tu);
@@ -1512,7 +1590,6 @@ static int snd_timer_user_tselect(struct file *file,
int err = 0;
tu = file->private_data;
- mutex_lock(&tu->tread_sem);
if (tu->timeri) {
snd_timer_close(tu->timeri);
tu->timeri = NULL;
@@ -1556,7 +1633,6 @@ static int snd_timer_user_tselect(struct file *file,
}
__err:
- mutex_unlock(&tu->tread_sem);
return err;
}
@@ -1769,7 +1845,7 @@ enum {
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
};
-static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct snd_timer_user *tu;
@@ -1786,17 +1862,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
{
int xarg;
- mutex_lock(&tu->tread_sem);
- if (tu->timeri) { /* too late */
- mutex_unlock(&tu->tread_sem);
+ if (tu->timeri) /* too late */
return -EBUSY;
- }
- if (get_user(xarg, p)) {
- mutex_unlock(&tu->tread_sem);
+ if (get_user(xarg, p))
return -EFAULT;
- }
tu->tread = xarg ? 1 : 0;
- mutex_unlock(&tu->tread_sem);
return 0;
}
case SNDRV_TIMER_IOCTL_GINFO:
@@ -1829,6 +1899,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return -ENOTTY;
}
+static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_timer_user *tu = file->private_data;
+ long ret;
+
+ mutex_lock(&tu->ioctl_lock);
+ ret = __snd_timer_user_ioctl(file, cmd, arg);
+ mutex_unlock(&tu->ioctl_lock);
+ return ret;
+}
+
static int snd_timer_user_fasync(int fd, struct file * file, int on)
{
struct snd_timer_user *tu;
@@ -1842,6 +1924,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
{
struct snd_timer_user *tu;
long result = 0, unit;
+ int qhead;
int err = 0;
tu = file->private_data;
@@ -1853,7 +1936,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN;
- break;
+ goto _error;
}
set_current_state(TASK_INTERRUPTIBLE);
@@ -1866,40 +1949,39 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
remove_wait_queue(&tu->qchange_sleep, &wait);
+ if (tu->disconnected) {
+ err = -ENODEV;
+ goto _error;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
- break;
+ goto _error;
}
}
+ qhead = tu->qhead++;
+ tu->qhead %= tu->queue_size;
spin_unlock_irq(&tu->qlock);
- if (err < 0)
- goto _error;
if (tu->tread) {
- if (copy_to_user(buffer, &tu->tqueue[tu->qhead++],
- sizeof(struct snd_timer_tread))) {
+ if (copy_to_user(buffer, &tu->tqueue[qhead],
+ sizeof(struct snd_timer_tread)))
err = -EFAULT;
- goto _error;
- }
} else {
- if (copy_to_user(buffer, &tu->queue[tu->qhead++],
- sizeof(struct snd_timer_read))) {
+ if (copy_to_user(buffer, &tu->queue[qhead],
+ sizeof(struct snd_timer_read)))
err = -EFAULT;
- goto _error;
- }
}
- tu->qhead %= tu->queue_size;
-
- result += unit;
- buffer += unit;
-
spin_lock_irq(&tu->qlock);
tu->qused--;
+ if (err < 0)
+ goto _error;
+ result += unit;
+ buffer += unit;
}
- spin_unlock_irq(&tu->qlock);
_error:
+ spin_unlock_irq(&tu->qlock);
return result > 0 ? result : err;
}
@@ -1915,6 +1997,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
mask = 0;
if (tu->qused)
mask |= POLLIN | POLLRDNORM;
+ if (tu->disconnected)
+ mask |= POLLERR;
return mask;
}
diff --git a/kernel/sound/core/timer_compat.c b/kernel/sound/core/timer_compat.c
index e05802ae6..2e908225d 100644
--- a/kernel/sound/core/timer_compat.c
+++ b/kernel/sound/core/timer_compat.c
@@ -70,13 +70,14 @@ static int snd_timer_user_status_compat(struct file *file,
struct snd_timer_status32 __user *_status)
{
struct snd_timer_user *tu;
- struct snd_timer_status status;
+ struct snd_timer_status32 status;
tu = file->private_data;
if (snd_BUG_ON(!tu->timeri))
return -ENXIO;
memset(&status, 0, sizeof(status));
- status.tstamp = tu->tstamp;
+ status.tstamp.tv_sec = tu->tstamp.tv_sec;
+ status.tstamp.tv_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost;
status.overrun = tu->overrun;
@@ -88,12 +89,21 @@ static int snd_timer_user_status_compat(struct file *file,
return 0;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has the same struct as x86-64 */
+#define snd_timer_user_status_x32(file, s) \
+ snd_timer_user_status(file, s)
+#endif /* CONFIG_X86_X32 */
+
/*
*/
enum {
SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32),
SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32),
+#ifdef CONFIG_X86_X32
+ SNDRV_TIMER_IOCTL_STATUS_X32 = _IOW('T', 0x14, struct snd_timer_status),
+#endif /* CONFIG_X86_X32 */
};
static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -122,6 +132,10 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_timer_user_info_compat(file, argp);
case SNDRV_TIMER_IOCTL_STATUS32:
return snd_timer_user_status_compat(file, argp);
+#ifdef CONFIG_X86_X32
+ case SNDRV_TIMER_IOCTL_STATUS_X32:
+ return snd_timer_user_status_x32(file, argp);
+#endif /* CONFIG_X86_X32 */
}
return -ENOIOCTLCMD;
}
diff --git a/kernel/sound/drivers/aloop.c b/kernel/sound/drivers/aloop.c
index 7f9126efc..54f348a4f 100644
--- a/kernel/sound/drivers/aloop.c
+++ b/kernel/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/kernel/sound/drivers/dummy.c b/kernel/sound/drivers/dummy.c
index d11baaf0f..a9f7a7570 100644
--- a/kernel/sound/drivers/dummy.c
+++ b/kernel/sound/drivers/dummy.c
@@ -109,6 +109,9 @@ struct dummy_timer_ops {
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
};
+#define get_dummy_ops(substream) \
+ (*(const struct dummy_timer_ops **)(substream)->runtime->private_data)
+
struct dummy_model {
const char *name;
int (*playback_constraints)(struct snd_pcm_runtime *runtime);
@@ -137,7 +140,6 @@ struct snd_dummy {
int iobox;
struct snd_kcontrol *cd_volume_ctl;
struct snd_kcontrol *cd_switch_ctl;
- const struct dummy_timer_ops *timer_ops;
};
/*
@@ -156,13 +158,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 +174,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 +184,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 +194,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 +204,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 +218,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,
@@ -231,6 +233,8 @@ struct dummy_model *dummy_models[] = {
*/
struct dummy_systimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
spinlock_t lock;
struct timer_list timer;
unsigned long base_time;
@@ -366,6 +370,8 @@ static struct dummy_timer_ops dummy_systimer_ops = {
*/
struct dummy_hrtimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
ktime_t base_time;
ktime_t period_time;
atomic_t running;
@@ -492,31 +498,25 @@ static struct dummy_timer_ops dummy_hrtimer_ops = {
static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- return dummy->timer_ops->start(substream);
+ return get_dummy_ops(substream)->start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- return dummy->timer_ops->stop(substream);
+ return get_dummy_ops(substream)->stop(substream);
}
return -EINVAL;
}
static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->prepare(substream);
+ return get_dummy_ops(substream)->prepare(substream);
}
static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->pointer(substream);
+ return get_dummy_ops(substream)->pointer(substream);
}
static struct snd_pcm_hardware dummy_pcm_hardware = {
@@ -562,17 +562,19 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
struct dummy_model *model = dummy->model;
struct snd_pcm_runtime *runtime = substream->runtime;
+ const struct dummy_timer_ops *ops;
int err;
- dummy->timer_ops = &dummy_systimer_ops;
+ ops = &dummy_systimer_ops;
#ifdef CONFIG_HIGH_RES_TIMERS
if (hrtimer)
- dummy->timer_ops = &dummy_hrtimer_ops;
+ ops = &dummy_hrtimer_ops;
#endif
- err = dummy->timer_ops->create(substream);
+ err = ops->create(substream);
if (err < 0)
return err;
+ get_dummy_ops(substream) = ops;
runtime->hw = dummy->pcm_hw;
if (substream->pcm->device & 1) {
@@ -594,7 +596,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
err = model->capture_constraints(substream->runtime);
}
if (err < 0) {
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return err;
}
return 0;
@@ -602,8 +604,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
static int dummy_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return 0;
}
@@ -914,7 +915,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 +1043,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/kernel/sound/drivers/opl4/Makefile b/kernel/sound/drivers/opl4/Makefile
index b94009b0b..c8eaa433d 100644
--- a/kernel/sound/drivers/opl4/Makefile
+++ b/kernel/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/kernel/sound/drivers/opl4/opl4_lib.c b/kernel/sound/drivers/opl4/opl4_lib.c
index 3b0ee42a5..89c7aa04b 100644
--- a/kernel/sound/drivers/opl4/opl4_lib.c
+++ b/kernel/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/kernel/sound/drivers/opl4/opl4_local.h b/kernel/sound/drivers/opl4/opl4_local.h
index 470e5a758..9a41bdebc 100644
--- a/kernel/sound/drivers/opl4/opl4_local.h
+++ b/kernel/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/kernel/sound/drivers/opl4/opl4_proc.c b/kernel/sound/drivers/opl4/opl4_proc.c
index 9b824bfc9..cd2c07fa2 100644
--- a/kernel/sound/drivers/opl4/opl4_proc.c
+++ b/kernel/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/kernel/sound/drivers/pcsp/pcsp.c b/kernel/sound/drivers/pcsp/pcsp.c
index d9647bd84..27e25bb78 100644
--- a/kernel/sound/drivers/pcsp/pcsp.c
+++ b/kernel/sound/drivers/pcsp/pcsp.c
@@ -42,16 +42,13 @@ struct snd_pcsp pcsp_chip;
static int snd_pcsp_create(struct snd_card *card)
{
static struct snd_device_ops ops = { };
- struct timespec tp;
- int err;
- int div, min_div, order;
-
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ unsigned int resolution = hrtimer_resolution;
+ int err, div, min_div, order;
if (!nopcm) {
- if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+ if (resolution > PCSP_MAX_PERIOD_NS) {
printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
- "(%linS)\n", tp.tv_nsec);
+ "(%unS)\n", resolution);
printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
"enabled.\n");
printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
@@ -59,13 +56,13 @@ static int snd_pcsp_create(struct snd_card *card)
}
}
- if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
+ if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS)
min_div = MIN_DIV;
else
min_div = MAX_DIV;
#if PCSP_DEBUG
- printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n",
- loops_per_jiffy, min_div, tp.tv_nsec);
+ printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%u\n",
+ loops_per_jiffy, min_div, resolution);
#endif
div = MAX_DIV / min_div;
diff --git a/kernel/sound/firewire/Kconfig b/kernel/sound/firewire/Kconfig
index ecec54778..e92a6d949 100644
--- a/kernel/sound/firewire/Kconfig
+++ b/kernel/sound/firewire/Kconfig
@@ -38,6 +38,7 @@ config SND_OXFW
* Mackie(Loud) Tapco Link.Firewire
* Mackie(Loud) d.2 pro/d.4 pro
* Mackie(Loud) U.420/U.420d
+ * TASCAM FireOne
To compile this driver as a module, choose M here: the module
will be called snd-oxfw.
@@ -95,6 +96,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,8 +116,36 @@ 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.
+config SND_FIREWIRE_DIGI00X
+ tristate "Digidesign Digi 002/003 family support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for Digidesign Digi 002/003 family.
+ * Digi 002 Console
+ * Digi 002 Rack
+ * Digi 003 Console
+ * Digi 003 Rack
+ * Digi 003 Rack+
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+ tristate "TASCAM FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for TASCAM.
+ * FW-1884
+ * FW-1082
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-tascam.
+
endif # SND_FIREWIRE
diff --git a/kernel/sound/firewire/Makefile b/kernel/sound/firewire/Makefile
index 8b37f084b..f5fb62551 100644
--- a/kernel/sound/firewire/Makefile
+++ b/kernel/sound/firewire/Makefile
@@ -1,6 +1,5 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
- fcp.o cmp.o amdtp.o
-snd-oxfw-objs := oxfw.o
+ fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o
snd-scs1x-objs := scs1x.o
@@ -11,3 +10,5 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
diff --git a/kernel/sound/firewire/amdtp-am824.c b/kernel/sound/firewire/amdtp-am824.c
new file mode 100644
index 000000000..bebddc60f
--- /dev/null
+++ b/kernel/sound/firewire/amdtp-am824.c
@@ -0,0 +1,465 @@
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM 0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+struct amdtp_am824 {
+ struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ int midi_fifo_limit;
+ int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+
+ u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+ u8 midi_position;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+
+ unsigned int frame_multiplier;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ * as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int midi_channels;
+ unsigned int i;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EINVAL;
+
+ if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+ return -EINVAL;
+
+ midi_channels = DIV_ROUND_UP(midi_ports, 8);
+ if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -EINVAL;
+
+ if (WARN_ON(amdtp_stream_running(s)) ||
+ WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+ WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+ return -EINVAL;
+
+ err = amdtp_stream_set_parameters(s, rate,
+ pcm_channels + midi_channels);
+ if (err < 0)
+ return err;
+
+ s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+ p->midi_ports = midi_ports;
+
+ /*
+ * In IEC 61883-6, one data block represents one event. In ALSA, one
+ * event equals to one PCM frame. But Dice has a quirk at higher
+ * sampling rate to transfer two PCM frames in one data block.
+ */
+ if (double_pcm_frames)
+ p->frame_multiplier = 2;
+ else
+ p->frame_multiplier = 1;
+
+ /* init the position map for PCM and MIDI channels */
+ for (i = 0; i < pcm_channels; i++)
+ p->pcm_positions[i] = i;
+ p->midi_position = p->pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ * of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (index < p->pcm_channels)
+ p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ * conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src >> 8) | 0x40000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src << 8) | 0x42000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/**
+ * amdtp_am824_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
+ * @format: the format of the ALSA PCM device
+ *
+ * The sample format must be set after the other parameters (rate/PCM channels/
+ * MIDI) and before the stream is started, and must not be changed while the
+ * stream is running.
+ */
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format);
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime: the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ if (err < 0)
+ return err;
+
+ /* AM824 in IEC 61883-6 can deliver 24bit data. */
+ return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data. This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (port < p->midi_ports)
+ ACCESS_ONCE(p->midi[port]) = midi;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled. This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate. One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0) /* common shortcut */
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ b = (u8 *)&buffer[p->midi_position];
+
+ port = (s->data_block_counter + f) % 8;
+ if (f < MAX_MIDI_RX_BLOCKS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL &&
+ snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+ midi_rate_use_one_byte(s, port);
+ b[0] = 0x81;
+ } else {
+ b[0] = 0x80;
+ b[1] = 0;
+ }
+ b[2] = 0;
+ b[3] = 0;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[p->midi_position];
+
+ len = b[0] - 0x80;
+ if ((1 <= len) && (len <= 3) && (p->midi[port]))
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int *syt)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks * p->frame_multiplier;
+ } else {
+ write_pcm_silence(s, buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ if (p->midi_ports)
+ write_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int *syt)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks * p->frame_multiplier;
+ } else {
+ pcm_frames = 0;
+ }
+
+ if (p->midi_ports)
+ read_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ * data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_data_blocks = process_tx_data_blocks;
+ else
+ process_data_blocks = process_rx_data_blocks;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_data_blocks,
+ sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/kernel/sound/firewire/amdtp-am824.h b/kernel/sound/firewire/amdtp-am824.h
new file mode 100644
index 000000000..73b07b310
--- /dev/null
+++ b/kernel/sound/firewire/amdtp-am824.h
@@ -0,0 +1,52 @@
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
+ SNDRV_PCM_FMTBIT_S32)
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM 64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI 1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s,
+ snd_pcm_format_t format);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags);
+#endif
diff --git a/kernel/sound/firewire/amdtp.c b/kernel/sound/firewire/amdtp-stream.c
index bf20593d3..ed2902609 100644
--- a/kernel/sound/firewire/amdtp.c
+++ b/kernel/sound/firewire/amdtp-stream.c
@@ -11,28 +11,14 @@
#include <linux/firewire.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/sched.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/rawmidi.h>
-#include "amdtp.h"
+#include "amdtp-stream.h"
#define TICKS_PER_CYCLE 3072
#define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
-/*
- * Nominally 3125 bytes/second, but the MIDI port's clock might be
- * 1% too slow, and the bus clock 100 ppm too fast.
- */
-#define MIDI_BYTES_PER_SECOND 3093
-
-/*
- * Several devices look only at the first eight data blocks.
- * In any case, this is more than enough for the MIDI data rate.
- */
-#define MAX_MIDI_RX_BLOCKS 8
-
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */
@@ -40,24 +26,24 @@
#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))
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM 0x10
#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
@@ -74,10 +60,23 @@ static void pcm_period_tasklet(unsigned long data);
* @unit: the target of the stream
* @dir: the direction of stream
* @flags: the packet transmission method to use
+ * @fmt: the value of fmt field in CIP header
+ * @process_data_blocks: callback handler to process data blocks
+ * @protocol_size: the size to allocate newly for protocol
*/
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
- enum amdtp_stream_direction dir, enum cip_flags flags)
+ enum amdtp_stream_direction dir, enum cip_flags flags,
+ unsigned int fmt,
+ amdtp_stream_process_data_blocks_t process_data_blocks,
+ unsigned int protocol_size)
{
+ if (process_data_blocks == NULL)
+ return -EINVAL;
+
+ s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+ if (!s->protocol)
+ return -ENOMEM;
+
s->unit = unit;
s->direction = dir;
s->flags = flags;
@@ -90,6 +89,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->callbacked = false;
s->sync_slave = NULL;
+ s->fmt = fmt;
+ s->process_data_blocks = process_data_blocks;
+
return 0;
}
EXPORT_SYMBOL(amdtp_stream_init);
@@ -101,6 +103,7 @@ EXPORT_SYMBOL(amdtp_stream_init);
void amdtp_stream_destroy(struct amdtp_stream *s)
{
WARN_ON(amdtp_stream_running(s));
+ kfree(s->protocol);
mutex_destroy(&s->mutex);
}
EXPORT_SYMBOL(amdtp_stream_destroy);
@@ -137,11 +140,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
{
int err;
- /* AM824 in IEC 61883-6 can deliver 24bit data */
- err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
- if (err < 0)
- goto end;
-
/*
* Currently firewire-lib processes 16 packets in one software
* interrupt callback. This equals to 2msec but actually the
@@ -186,39 +184,25 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
* amdtp_stream_set_parameters - set stream parameters
* @s: the AMDTP stream to configure
* @rate: the sample rate
- * @pcm_channels: the number of PCM samples in each data block, to be encoded
- * as AM824 multi-bit linear audio
- * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @data_block_quadlets: the size of a data block in quadlet unit
*
* The parameters must be set before the stream is started, and must not be
* changed while the stream is running.
*/
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports)
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets)
{
- unsigned int i, sfc, midi_channels;
-
- midi_channels = DIV_ROUND_UP(midi_ports, 8);
-
- if (WARN_ON(amdtp_stream_running(s)) |
- WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
- WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
- return;
+ unsigned int sfc;
- for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
+ for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
if (amdtp_rate_table[sfc] == rate)
- goto sfc_found;
- WARN_ON(1);
- return;
+ break;
+ }
+ if (sfc == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
-sfc_found:
- s->pcm_channels = pcm_channels;
s->sfc = sfc;
- s->data_block_quadlets = s->pcm_channels + midi_channels;
- s->midi_ports = midi_ports;
-
+ s->data_block_quadlets = data_block_quadlets;
s->syt_interval = amdtp_syt_intervals[sfc];
/* default buffering in the device */
@@ -227,18 +211,7 @@ sfc_found:
/* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
- /* init the position map for PCM and MIDI channels */
- for (i = 0; i < pcm_channels; i++)
- s->pcm_positions[i] = i;
- s->midi_position = s->pcm_channels;
-
- /*
- * We do not know the actual MIDI FIFO size of most devices. Just
- * assume two bytes, i.e., one byte can be received over the bus while
- * the previous one is transmitted over MIDI.
- * (The value here is adjusted for midi_ratelimit_per_packet().)
- */
- s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+ return 0;
}
EXPORT_SYMBOL(amdtp_stream_set_parameters);
@@ -251,55 +224,14 @@ 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;
-}
-EXPORT_SYMBOL(amdtp_stream_get_max_payload);
+ unsigned int multiplier = 1;
-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);
+ if (s->flags & CIP_JUMBO_PAYLOAD)
+ multiplier = 5;
-/**
- * amdtp_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP stream to configure
- * @format: the format of the ALSA PCM device
- *
- * The sample format must be set after the other parameters (rate/PCM channels/
- * MIDI) and before the stream is started, and must not be changed while the
- * stream is running.
- */
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
- snd_pcm_format_t format)
-{
- if (WARN_ON(amdtp_stream_pcm_running(s)))
- return;
-
- switch (format) {
- default:
- WARN_ON(1);
- /* fall through */
- case SNDRV_PCM_FORMAT_S16:
- if (s->direction == AMDTP_OUT_STREAM) {
- s->transfer_samples = amdtp_write_s16;
- break;
- }
- WARN_ON(1);
- /* fall through */
- case SNDRV_PCM_FORMAT_S32:
- if (s->direction == AMDTP_OUT_STREAM)
- s->transfer_samples = amdtp_write_s32;
- else
- s->transfer_samples = amdtp_read_s32;
- break;
- }
+ return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
-EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
/**
* amdtp_stream_pcm_prepare - prepare PCM device for running
@@ -316,17 +248,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 +276,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,182 +335,12 @@ 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)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- const u32 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- buffer[s->pcm_positions[c]] =
- cpu_to_be32((*src >> 8) | 0x40000000);
- src++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- src = (void *)runtime->dma_area;
- }
-}
-
-static void amdtp_write_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;
- const u16 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- buffer[s->pcm_positions[c]] =
- cpu_to_be32((*src << 8) | 0x42000000);
- src++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- src = (void *)runtime->dma_area;
- }
-}
-
-static void amdtp_read_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;
- u32 *dst;
-
- channels = s->pcm_channels;
- dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
- dst++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- dst = (void *)runtime->dma_area;
- }
-}
-
-static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int i, c;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < s->pcm_channels; ++c)
- buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
- buffer += s->data_block_quadlets;
- }
-}
-
-/*
- * To avoid sending MIDI bytes at too high a rate, assume that the receiving
- * device has a FIFO, and track how much it is filled. This values increases
- * by one whenever we send one byte in a packet, but the FIFO empties at
- * a constant rate independent of our packet rate. One packet has syt_interval
- * samples, so the number of bytes that empty out of the FIFO, per packet(!),
- * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
- * fractional values, the values in midi_fifo_used[] are measured in bytes
- * multiplied by the sample rate.
- */
-static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
-{
- int used;
-
- used = s->midi_fifo_used[port];
- if (used == 0) /* common shortcut */
- return true;
-
- used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
- used = max(used, 0);
- s->midi_fifo_used[port] = used;
-
- return used < s->midi_fifo_limit;
-}
-
-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)
-{
- unsigned int f, port;
- u8 *b;
-
- for (f = 0; f < frames; f++) {
- b = (u8 *)&buffer[s->midi_position];
-
- port = (s->data_block_counter + f) % 8;
- if (f < MAX_MIDI_RX_BLOCKS &&
- midi_ratelimit_per_packet(s, port) &&
- s->midi[port] != NULL &&
- snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
- midi_rate_use_one_byte(s, port);
- b[0] = 0x81;
- } else {
- b[0] = 0x80;
- b[1] = 0;
- }
- b[2] = 0;
- b[3] = 0;
-
- buffer += s->data_block_quadlets;
- }
-}
-
-static void amdtp_pull_midi(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int f, port;
- int len;
- u8 *b;
-
- for (f = 0; f < frames; f++) {
- port = (s->data_block_counter + f) % 8;
- b = (u8 *)&buffer[s->midi_position];
-
- len = b[0] - 0x80;
- if ((1 <= len) && (len <= 3) && (s->midi[port]))
- snd_rawmidi_receive(s->midi[port], b + 1, len);
-
- buffer += s->data_block_quadlets;
- }
-}
-
static void update_pcm_pointers(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
unsigned int frames)
{
unsigned int ptr;
- /*
- * In IEC 61883-6, one data block represents one event. In ALSA, one
- * event equals to one PCM frame. But Dice has a quirk to transfer
- * two PCM frames in one data block.
- */
- if (s->double_pcm_frames)
- frames *= 2;
-
ptr = s->pcm_buffer_pointer + frames;
if (ptr >= pcm->runtime->buffer_size)
ptr -= pcm->runtime->buffer_size;
@@ -633,58 +404,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;
+ unsigned int pcm_frames;
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;
+ pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+
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);
- 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);
- if (s->midi_ports)
- amdtp_fill_midi(s, buffer, data_blocks);
+ buffer[1] = cpu_to_be32(CIP_EOH |
+ ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+ ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+ (syt & CIP_SYT_MASK));
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);
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
+
+ /* 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, unsigned int syt)
{
u32 cip_header[2];
- unsigned int data_blocks, data_block_quadlets, data_block_counter,
- dbc_interval;
- struct snd_pcm_substream *pcm = NULL;
+ unsigned int fmt, fdf;
+ unsigned int data_block_quadlets, data_block_counter, dbc_interval;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
bool lost;
cip_header[0] = be32_to_cpu(buffer[0]);
@@ -695,38 +456,50 @@ static void handle_in_packet(struct amdtp_stream *s,
* For convenience, also check FMT field is AM824 or not.
*/
if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
- ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
+ *data_blocks = 0;
+ pcm_frames = 0;
+ goto end;
+ }
+
+ /* Check valid protocol or not. */
+ fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+ if (fmt != s->fmt) {
+ dev_info_ratelimited(&s->unit->device,
+ "Detect unexpected protocol: %08x %08x\n",
+ cip_header[0], cip_header[1]);
+ *data_blocks = 0;
+ pcm_frames = 0;
goto end;
}
/* Calculate data blocks */
+ fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 ||
- ((cip_header[1] & CIP_FDF_MASK) ==
- (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) {
- data_blocks = 0;
+ (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
+ *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;
@@ -737,49 +510,38 @@ 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) {
- buffer += 2;
-
- pcm = ACCESS_ONCE(s->pcm);
- if (pcm)
- s->transfer_samples(s, pcm, buffer, data_blocks);
-
- if (s->midi_ports)
- amdtp_pull_midi(s, buffer, data_blocks);
- }
+ pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
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);
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
- 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,
@@ -788,6 +550,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.
@@ -798,8 +564,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);
}
@@ -808,32 +581,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;
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+ if (handle_in_packet(s, payload_quadlets, buffer,
+ &data_blocks, syt) < 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;
@@ -873,7 +669,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;
@@ -1014,8 +810,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/kernel/sound/firewire/amdtp.h b/kernel/sound/firewire/amdtp-stream.h
index 25c905537..8775704a3 100644
--- a/kernel/sound/firewire/amdtp.h
+++ b/kernel/sound/firewire/amdtp-stream.h
@@ -4,6 +4,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
+#include <linux/sched.h>
#include <sound/asound.h>
#include "packets-buffer.h"
@@ -29,6 +30,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 +44,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,
};
/**
@@ -76,100 +81,78 @@ enum cip_sfc {
CIP_SFC_COUNT
};
-#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
-
-#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
- SNDRV_PCM_FMTBIT_S32)
-
-
-/*
- * This module supports maximum 64 PCM channels for one PCM stream
- * This is for our convenience.
- */
-#define AMDTP_MAX_CHANNELS_FOR_PCM 64
-
-/*
- * AMDTP packet can include channels for MIDI conformant data.
- * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
- * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
- *
- * This module supports maximum 1 MIDI conformant data channels.
- * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
- */
-#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
-
struct fw_unit;
struct fw_iso_context;
struct snd_pcm_substream;
struct snd_pcm_runtime;
-struct snd_rawmidi_substream;
enum amdtp_stream_direction {
AMDTP_OUT_STREAM = 0,
AMDTP_IN_STREAM
};
+struct amdtp_stream;
+typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
+ struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt);
struct amdtp_stream {
struct fw_unit *unit;
enum cip_flags flags;
enum amdtp_stream_direction direction;
- struct fw_iso_context *context;
struct mutex mutex;
- enum cip_sfc sfc;
- unsigned int data_block_quadlets;
- unsigned int pcm_channels;
- unsigned int midi_ports;
- void (*transfer_samples)(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
- u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
- u8 midi_position;
-
- unsigned int syt_interval;
- unsigned int transfer_delay;
- unsigned int source_node_id_field;
+ /* For packet processing. */
+ struct fw_iso_context *context;
struct iso_packets_buffer buffer;
-
- struct snd_pcm_substream *pcm;
- struct tasklet_struct period_tasklet;
-
int packet_index;
+
+ /* For CIP headers. */
+ unsigned int source_node_id_field;
+ unsigned int data_block_quadlets;
unsigned int data_block_counter;
+ unsigned int fmt;
+ unsigned int fdf;
+ /* quirk: fixed interval of dbc between previos/current packets. */
+ unsigned int tx_dbc_interval;
+ /* quirk: indicate the value of dbc field in a first packet. */
+ unsigned int tx_first_dbc;
+ /* Internal flags. */
+ enum cip_sfc sfc;
+ unsigned int syt_interval;
+ unsigned int transfer_delay;
unsigned int data_block_state;
-
unsigned int last_syt_offset;
unsigned int syt_offset_state;
+ /* For a PCM substream processing. */
+ struct snd_pcm_substream *pcm;
+ struct tasklet_struct period_tasklet;
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
bool pointer_flush;
- bool double_pcm_frames;
-
- struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
- int midi_fifo_limit;
- int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-
- /* quirk: fixed interval of dbc between previos/current packets. */
- unsigned int tx_dbc_interval;
- /* quirk: indicate the value of dbc field in a first packet. */
- unsigned int tx_first_dbc;
+ /* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
struct amdtp_stream *sync_slave;
+
+ /* For backends to process data blocks. */
+ void *protocol;
+ amdtp_stream_process_data_blocks_t process_data_blocks;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
- enum amdtp_stream_direction dir,
- enum cip_flags flags);
+ enum amdtp_stream_direction dir, enum cip_flags flags,
+ unsigned int fmt,
+ amdtp_stream_process_data_blocks_t process_data_blocks,
+ unsigned int protocol_size);
void amdtp_stream_destroy(struct amdtp_stream *s);
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports);
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
@@ -178,8 +161,7 @@ void amdtp_stream_stop(struct amdtp_stream *s);
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
- snd_pcm_format_t format);
+
void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
@@ -236,24 +218,6 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
ACCESS_ONCE(s->pcm) = pcm;
}
-/**
- * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
- * @s: the AMDTP stream
- * @port: index of MIDI port
- * @midi: the MIDI device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of MIDI data. This function should be called from the MIDI
- * device's .trigger callback.
- */
-static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
- unsigned int port,
- struct snd_rawmidi_substream *midi)
-{
- if (port < s->midi_ports)
- ACCESS_ONCE(s->midi[port]) = midi;
-}
-
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{
return sfc & 1;
diff --git a/kernel/sound/firewire/bebob/Makefile b/kernel/sound/firewire/bebob/Makefile
index 6cf470c80..af7ed6643 100644
--- a/kernel/sound/firewire/bebob/Makefile
+++ b/kernel/sound/firewire/bebob/Makefile
@@ -1,4 +1,4 @@
snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
bebob_focusrite.o bebob_maudio.o bebob.o
-obj-m += snd-bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
diff --git a/kernel/sound/firewire/bebob/bebob.c b/kernel/sound/firewire/bebob/bebob.c
index 611b7dae7..091290d1f 100644
--- a/kernel/sound/firewire/bebob/bebob.c
+++ b/kernel/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
@@ -40,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_EDIROL 0x000040ab
#define VEN_PRESONUS 0x00000a92
#define VEN_BRIDGECO 0x000007f5
-#define VEN_MACKIE 0x0000000f
+#define VEN_MACKIE1 0x0000000f
+#define VEN_MACKIE2 0x00000ff2
#define VEN_STANTON 0x00001260
#define VEN_TASCAM 0x0000022e
#define VEN_BEHRINGER 0x00001564
@@ -57,6 +59,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 +75,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 +108,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);
@@ -325,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit)
snd_card_free_when_closed(bebob->card);
}
-static struct snd_bebob_rate_spec normal_rate_spec = {
+static const struct snd_bebob_rate_spec normal_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate
};
@@ -351,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* BridgeCo, Audio5 */
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
/* Mackie, d.2 (Firewire Option) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
/* Stanton, ScratchAmp */
SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
/* Tascam, IF-FW DM */
@@ -364,6 +374,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 +447,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/kernel/sound/firewire/bebob/bebob.h b/kernel/sound/firewire/bebob/bebob.h
index dfbcd2331..4d8fcc78e 100644
--- a/kernel/sound/firewire/bebob/bebob.h
+++ b/kernel/sound/firewire/bebob/bebob.h
@@ -31,7 +31,7 @@
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
/* basic register addresses on DM1000/DM1100/DM1500 */
@@ -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 {
@@ -65,9 +70,9 @@ struct snd_bebob_meter_spec {
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
};
struct snd_bebob_spec {
- struct snd_bebob_clock_spec *clock;
- struct snd_bebob_rate_spec *rate;
- struct snd_bebob_meter_spec *meter;
+ const struct snd_bebob_clock_spec *clock;
+ const struct snd_bebob_rate_spec *rate;
+ const struct snd_bebob_meter_spec *meter;
};
struct snd_bebob {
@@ -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);
@@ -227,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
/* model specific operations */
-extern struct snd_bebob_spec phase88_rack_spec;
-extern struct snd_bebob_spec phase24_series_spec;
-extern struct snd_bebob_spec yamaha_go_spec;
-extern struct snd_bebob_spec saffirepro_26_spec;
-extern struct snd_bebob_spec saffirepro_10_spec;
-extern struct snd_bebob_spec saffire_le_spec;
-extern struct snd_bebob_spec saffire_spec;
-extern struct snd_bebob_spec maudio_fw410_spec;
-extern struct snd_bebob_spec maudio_audiophile_spec;
-extern struct snd_bebob_spec maudio_solo_spec;
-extern struct snd_bebob_spec maudio_ozonic_spec;
-extern struct snd_bebob_spec maudio_nrv10_spec;
-extern struct snd_bebob_spec maudio_special_spec;
+extern const struct snd_bebob_spec phase88_rack_spec;
+extern const struct snd_bebob_spec phase24_series_spec;
+extern const struct snd_bebob_spec yamaha_go_spec;
+extern const struct snd_bebob_spec saffirepro_26_spec;
+extern const struct snd_bebob_spec saffirepro_10_spec;
+extern const struct snd_bebob_spec saffire_le_spec;
+extern const struct snd_bebob_spec saffire_spec;
+extern const struct snd_bebob_spec maudio_fw410_spec;
+extern const struct snd_bebob_spec maudio_audiophile_spec;
+extern const struct snd_bebob_spec maudio_solo_spec;
+extern const struct snd_bebob_spec maudio_ozonic_spec;
+extern const struct snd_bebob_spec maudio_nrv10_spec;
+extern const struct snd_bebob_spec maudio_special_spec;
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
diff --git a/kernel/sound/firewire/bebob/bebob_focusrite.c b/kernel/sound/firewire/bebob/bebob_focusrite.c
index fc67c1b7c..f11090057 100644
--- a/kernel/sound/firewire/bebob/bebob_focusrite.c
+++ b/kernel/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];
@@ -194,9 +200,10 @@ end:
return err;
}
-struct snd_bebob_spec saffire_le_spec;
-static const char *const saffire_both_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
+const struct snd_bebob_spec saffire_le_spec;
+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)
@@ -222,7 +229,7 @@ static const char *const saffire_meter_labels[] = {
static int
saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int channels;
u64 offset;
int err;
@@ -253,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
return err;
}
-static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
.get = &saffirepro_both_clk_freq_get,
.set = &saffirepro_both_clk_freq_set,
};
/* 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,
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+ .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 = {
+const struct snd_bebob_spec saffirepro_26_spec = {
.clock = &saffirepro_26_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
/* 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,
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+ .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 = {
+const struct snd_bebob_spec saffirepro_10_spec = {
.clock = &saffirepro_10_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
-static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.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,
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
+ .num = ARRAY_SIZE(saffire_both_clk_src_types),
+ .types = saffire_both_clk_src_types,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */
-static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
.num = ARRAY_SIZE(saffire_le_meter_labels),
.labels = saffire_le_meter_labels,
.get = &saffire_meter_get,
};
-struct snd_bebob_spec saffire_le_spec = {
+const struct snd_bebob_spec saffire_le_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_le_meter_spec
};
/* Saffire */
-static struct snd_bebob_meter_spec saffire_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
.num = ARRAY_SIZE(saffire_meter_labels),
.labels = saffire_meter_labels,
.get = &saffire_meter_get,
};
-struct snd_bebob_spec saffire_spec = {
+const struct snd_bebob_spec saffire_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_meter_spec
diff --git a/kernel/sound/firewire/bebob/bebob_maudio.c b/kernel/sound/firewire/bebob/bebob_maudio.c
index 9ee25a63f..07e5abdbc 100644
--- a/kernel/sound/firewire/bebob/bebob_maudio.c
+++ b/kernel/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);
@@ -619,7 +628,7 @@ static const char *const special_meter_labels[] = {
static int
special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
{
- u16 *buf;
+ __be16 *buf;
unsigned int i, c, channels;
int err;
@@ -678,7 +687,7 @@ static const char *const nrv10_meter_labels[] = {
static int
normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int c, channels;
int err;
@@ -703,85 +712,85 @@ end:
}
/* for special customized devices */
-static struct snd_bebob_rate_spec special_rate_spec = {
+static const struct snd_bebob_rate_spec special_rate_spec = {
.get = &special_get_rate,
.set = &special_set_rate,
};
-static struct snd_bebob_clock_spec special_clk_spec = {
- .num = ARRAY_SIZE(special_clk_labels),
- .labels = special_clk_labels,
+static const struct snd_bebob_clock_spec special_clk_spec = {
+ .num = ARRAY_SIZE(special_clk_types),
+ .types = special_clk_types,
.get = &special_clk_get,
};
-static struct snd_bebob_meter_spec special_meter_spec = {
+static const struct snd_bebob_meter_spec special_meter_spec = {
.num = ARRAY_SIZE(special_meter_labels),
.labels = special_meter_labels,
.get = &special_meter_get
};
-struct snd_bebob_spec maudio_special_spec = {
+const struct snd_bebob_spec maudio_special_spec = {
.clock = &special_clk_spec,
.rate = &special_rate_spec,
.meter = &special_meter_spec
};
/* Firewire 410 specification */
-static struct snd_bebob_rate_spec usual_rate_spec = {
+static const struct snd_bebob_rate_spec usual_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-static struct snd_bebob_meter_spec fw410_meter_spec = {
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
.num = ARRAY_SIZE(fw410_meter_labels),
.labels = fw410_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_fw410_spec = {
+const struct snd_bebob_spec maudio_fw410_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &fw410_meter_spec
};
/* Firewire Audiophile specification */
-static struct snd_bebob_meter_spec audiophile_meter_spec = {
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
.num = ARRAY_SIZE(audiophile_meter_labels),
.labels = audiophile_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_audiophile_spec = {
+const struct snd_bebob_spec maudio_audiophile_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &audiophile_meter_spec
};
/* Firewire Solo specification */
-static struct snd_bebob_meter_spec solo_meter_spec = {
+static const struct snd_bebob_meter_spec solo_meter_spec = {
.num = ARRAY_SIZE(solo_meter_labels),
.labels = solo_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_solo_spec = {
+const struct snd_bebob_spec maudio_solo_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &solo_meter_spec
};
/* Ozonic specification */
-static struct snd_bebob_meter_spec ozonic_meter_spec = {
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
.num = ARRAY_SIZE(ozonic_meter_labels),
.labels = ozonic_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_ozonic_spec = {
+const struct snd_bebob_spec maudio_ozonic_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &ozonic_meter_spec
};
/* NRV10 specification */
-static struct snd_bebob_meter_spec nrv10_meter_spec = {
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
.num = ARRAY_SIZE(nrv10_meter_labels),
.labels = nrv10_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_nrv10_spec = {
+const struct snd_bebob_spec maudio_nrv10_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &nrv10_meter_spec
diff --git a/kernel/sound/firewire/bebob/bebob_midi.c b/kernel/sound/firewire/bebob/bebob_midi.c
index 63343d578..90d95be49 100644
--- a/kernel/sound/firewire/bebob/bebob_midi.c
+++ b/kernel/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);
@@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&bebob->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&bebob->tx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&bebob->tx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
@@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&bebob->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&bebob->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&bebob->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
diff --git a/kernel/sound/firewire/bebob/bebob_pcm.c b/kernel/sound/firewire/bebob/bebob_pcm.c
index 4a55561ed..ef224d6f5 100644
--- a/kernel/sound/firewire/bebob/bebob_pcm.c
+++ b/kernel/sound/firewire/bebob/bebob_pcm.c
@@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
s = &bebob->tx_stream;
formations = bebob->tx_stream_formations;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
s = &bebob->rx_stream;
formations = bebob->rx_stream_formations;
}
@@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
@@ -155,9 +155,9 @@ static int
pcm_open(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+ const 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);
@@ -211,26 +211,38 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_bebob *bebob = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->capture_substreams);
- amdtp_stream_set_pcm_format(&bebob->tx_stream,
- params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ atomic_inc(&bebob->substreams_counter);
+
+ amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
+
+ return 0;
}
static int
pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_bebob *bebob = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->playback_substreams);
- amdtp_stream_set_pcm_format(&bebob->rx_stream,
- params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ atomic_inc(&bebob->substreams_counter);
+
+ amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
+
+ return 0;
}
static int
@@ -239,7 +251,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 +263,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/kernel/sound/firewire/bebob/bebob_proc.c b/kernel/sound/firewire/bebob/bebob_proc.c
index 335da6450..ec24f9679 100644
--- a/kernel/sound/firewire/bebob/bebob_proc.c
+++ b/kernel/sound/firewire/bebob/bebob_proc.c
@@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
u32 *buf;
unsigned int i, c, channels, size;
@@ -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;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ 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/kernel/sound/firewire/bebob/bebob_stream.c b/kernel/sound/firewire/bebob/bebob_stream.c
index 98e4fc812..5022c9b97 100644
--- a/kernel/sound/firewire/bebob/bebob_stream.c
+++ b/kernel/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
/*
@@ -47,14 +47,16 @@ static const unsigned int bridgeco_freq_table[] = {
[6] = 0x07,
};
-static unsigned int
-get_formation_index(unsigned int rate)
+static int
+get_formation_index(unsigned int rate, unsigned int *index)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
- if (snd_bebob_rate_table[i] == rate)
- return i;
+ if (snd_bebob_rate_table[i] == rate) {
+ *index = i;
+ return 0;
+ }
}
return -EINVAL;
}
@@ -116,16 +118,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;
+ const 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 +144,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 +153,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 +176,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;
}
@@ -281,7 +340,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
err = -ENOSYS;
goto end;
}
- s->midi_position = stm_pos;
+ amdtp_am824_set_midi_position(s, stm_pos);
midi = stm_pos;
break;
/* for PCM data channel */
@@ -297,11 +356,12 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
case 0x09: /* Digital */
default:
location = pcm + sec_loc;
- if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+ if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
- s->pcm_positions[location] = stm_pos;
+ amdtp_am824_set_pcm_position(s, location,
+ stm_pos);
break;
}
}
@@ -367,15 +427,24 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
goto end;
/* confirm params for both streams */
- index = get_formation_index(rate);
+ err = get_formation_index(rate, &index);
+ if (err < 0)
+ goto end;
pcm_channels = bebob->tx_stream_formations[index].pcm;
midi_channels = bebob->tx_stream_formations[index].midi;
- amdtp_stream_set_parameters(&bebob->tx_stream,
- rate, pcm_channels, midi_channels * 8);
+ err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
+ pcm_channels, midi_channels * 8,
+ false);
+ if (err < 0)
+ goto end;
+
pcm_channels = bebob->rx_stream_formations[index].pcm;
midi_channels = bebob->rx_stream_formations[index].midi;
- amdtp_stream_set_parameters(&bebob->rx_stream,
- rate, pcm_channels, midi_channels * 8);
+ err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
+ pcm_channels, midi_channels * 8,
+ false);
+ if (err < 0)
+ goto end;
/* establish connections for both streams */
err = cmp_connection_establish(&bebob->out_conn,
@@ -417,8 +486,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;
}
@@ -457,8 +542,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
if (err < 0)
goto end;
- err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
- AMDTP_IN_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
+ AMDTP_IN_STREAM, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(&bebob->tx_stream);
destroy_both_connections(bebob);
@@ -467,6 +552,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
@@ -475,8 +571,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
if (bebob->maudio_special_quirk)
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
- err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
- AMDTP_OUT_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
+ AMDTP_OUT_STREAM, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(&bebob->tx_stream);
amdtp_stream_destroy(&bebob->rx_stream);
@@ -488,9 +584,8 @@ end:
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
- struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const 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 +610,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 +619,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 +722,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 +748,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);
@@ -790,8 +876,8 @@ parse_stream_formation(u8 *buf, unsigned int len,
}
}
- if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
- formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
return -ENOSYS;
return 0;
@@ -885,7 +971,7 @@ end:
int snd_bebob_stream_discover(struct snd_bebob *bebob)
{
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
enum avc_bridgeco_plug_type type;
unsigned int i;
diff --git a/kernel/sound/firewire/bebob/bebob_terratec.c b/kernel/sound/firewire/bebob/bebob_terratec.c
index ad635004d..c38358b82 100644
--- a/kernel/sound/firewire/bebob/bebob_terratec.c
+++ b/kernel/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,39 +36,49 @@ 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 = {
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
/* 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,
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
+ .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 = {
+const struct snd_bebob_spec phase88_rack_spec = {
.clock = &phase88_rack_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
};
/* '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,
+static const struct snd_bebob_clock_spec phase24_series_clk = {
+ .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 = {
+const struct snd_bebob_spec phase24_series_spec = {
.clock = &phase24_series_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
diff --git a/kernel/sound/firewire/bebob/bebob_yamaha.c b/kernel/sound/firewire/bebob/bebob_yamaha.c
index ef1fe3823..90d4404f7 100644
--- a/kernel/sound/firewire/bebob/bebob_yamaha.c
+++ b/kernel/sound/firewire/bebob/bebob_yamaha.c
@@ -28,22 +28,34 @@
* 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,
+static const struct snd_bebob_clock_spec clock_spec = {
+ .num = ARRAY_SIZE(clk_src_types),
+ .types = clk_src_types,
.get = &clk_src_get,
};
-static struct snd_bebob_rate_spec rate_spec = {
+static const struct snd_bebob_rate_spec rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-struct snd_bebob_spec yamaha_go_spec = {
+const struct snd_bebob_spec yamaha_go_spec = {
.clock = &clock_spec,
.rate = &rate_spec,
.meter = NULL
diff --git a/kernel/sound/firewire/dice/Makefile b/kernel/sound/firewire/dice/Makefile
index 9ef228ef7..55b4be9b0 100644
--- a/kernel/sound/firewire/dice/Makefile
+++ b/kernel/sound/firewire/dice/Makefile
@@ -1,3 +1,3 @@
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
dice-pcm.o dice-hwdep.o dice.o
-obj-m += snd-dice.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/kernel/sound/firewire/dice/dice-midi.c b/kernel/sound/firewire/dice/dice-midi.c
index fe43ce791..151b09f24 100644
--- a/kernel/sound/firewire/dice/dice-midi.c
+++ b/kernel/sound/firewire/dice/dice-midi.c
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream,
substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&dice->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&dice->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&dice->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&dice->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
}
diff --git a/kernel/sound/firewire/dice/dice-pcm.c b/kernel/sound/firewire/dice/dice-pcm.c
index f77714511..9b3431999 100644
--- a/kernel/sound/firewire/dice/dice-pcm.c
+++ b/kernel/sound/firewire/dice/dice-pcm.c
@@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice,
SNDRV_PCM_INFO_BLOCK_TRANSFER;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+ hw->formats = AM824_IN_PCM_FORMAT_BITS;
stream = &dice->tx_stream;
pcm_channels = dice->tx_channels;
} else {
- hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ hw->formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &dice->rx_stream;
pcm_channels = dice->rx_channels;
}
@@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
}
@@ -230,6 +230,12 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&dice->mutex);
@@ -237,16 +243,20 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_stream_set_pcm_format(&dice->tx_stream,
- params_format(hw_params));
+ amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
static int playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&dice->mutex);
@@ -254,11 +264,9 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_stream_set_pcm_format(&dice->rx_stream,
- params_format(hw_params));
+ amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
static int capture_hw_free(struct snd_pcm_substream *substream)
diff --git a/kernel/sound/firewire/dice/dice-stream.c b/kernel/sound/firewire/dice/dice-stream.c
index 07dbd01d7..a6a39f7ef 100644
--- a/kernel/sound/firewire/dice/dice-stream.c
+++ b/kernel/sound/firewire/dice/dice-stream.c
@@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
static void release_resources(struct snd_dice *dice,
struct fw_iso_resources *resources)
{
- unsigned int channel;
+ __be32 channel;
/* Reset channel number */
channel = cpu_to_be32((u32)-1);
if (resources == &dice->tx_resources)
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
else
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
fw_iso_resources_free(resources);
}
@@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice,
struct fw_iso_resources *resources,
unsigned int max_payload_bytes)
{
- unsigned int channel;
+ __be32 channel;
int err;
err = fw_iso_resources_allocate(resources, max_payload_bytes,
@@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice,
channel = cpu_to_be32(resources->channel);
if (resources == &dice->tx_resources)
err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
else
err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
if (err < 0)
release_resources(dice, resources);
end:
@@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
{
struct fw_iso_resources *resources;
unsigned int i, mode, pcm_chs, midi_ports;
+ bool double_pcm_frames;
int err;
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
@@ -125,21 +126,24 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
* For this quirk, blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL.
*/
- if (mode > 1) {
+ double_pcm_frames = mode > 1;
+ if (double_pcm_frames) {
rate /= 2;
pcm_chs *= 2;
- stream->double_pcm_frames = true;
- } else {
- stream->double_pcm_frames = false;
}
- amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
- if (mode > 1) {
+ err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+ double_pcm_frames);
+ if (err < 0)
+ goto end;
+
+ if (double_pcm_frames) {
pcm_chs /= 2;
for (i = 0; i < pcm_chs; i++) {
- stream->pcm_positions[i] = i * 2;
- stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
+ amdtp_am824_set_pcm_position(stream, i, i * 2);
+ amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+ i * 2 + 1);
}
}
@@ -302,7 +306,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
goto end;
resources->channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
diff --git a/kernel/sound/firewire/dice/dice.c b/kernel/sound/firewire/dice/dice.c
index 70a111d7f..0cda05c72 100644
--- a/kernel/sound/firewire/dice/dice.c
+++ b/kernel/sound/firewire/dice/dice.c
@@ -12,9 +12,11 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
#define OUI_WEISS 0x001c6a
+#define OUI_LOUD 0x000ff2
#define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00
+#define LOUD_CATEGORY_ID 0x10
static int dice_interface_check(struct fw_unit *unit)
{
@@ -29,7 +31,8 @@ static int dice_interface_check(struct fw_unit *unit)
struct fw_csr_iterator it;
int key, val, vendor = -1, model = -1, err;
unsigned int category, i;
- __be32 *pointers, value;
+ __be32 *pointers;
+ u32 value;
__be32 version;
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
@@ -56,6 +59,8 @@ static int dice_interface_check(struct fw_unit *unit)
}
if (vendor == OUI_WEISS)
category = WEISS_CATEGORY_ID;
+ else if (vendor == OUI_LOUD)
+ category = LOUD_CATEGORY_ID;
else
category = DICE_CATEGORY_ID;
if (device->config_rom[3] != ((vendor << 8) | category) ||
diff --git a/kernel/sound/firewire/dice/dice.h b/kernel/sound/firewire/dice/dice.h
index ecf5dc862..101550ac1 100644
--- a/kernel/sound/firewire/dice/dice.h
+++ b/kernel/sound/firewire/dice/dice.h
@@ -34,7 +34,7 @@
#include <sound/pcm_params.h>
#include <sound/rawmidi.h>
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../iso-resources.h"
#include "../lib.h"
#include "dice-interface.h"
diff --git a/kernel/sound/firewire/digi00x/Makefile b/kernel/sound/firewire/digi00x/Makefile
new file mode 100644
index 000000000..1123e68c8
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/Makefile
@@ -0,0 +1,4 @@
+snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+ digi00x-pcm.o digi00x-hwdep.o \
+ digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/kernel/sound/firewire/digi00x/amdtp-dot.c b/kernel/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644
index 000000000..b02a5e8ca
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/amdtp-dot.c
@@ -0,0 +1,442 @@
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM 0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+ u8 carry;
+ u8 idx;
+ unsigned int off;
+};
+
+struct amdtp_dot {
+ unsigned int pcm_channels;
+ struct dot_state state;
+
+ unsigned int midi_ports;
+ /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
+ struct snd_rawmidi_substream *midi[2];
+ int midi_fifo_used[2];
+ int midi_fifo_limit;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static const u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+ /*
+ * the length of the added pattern only depends on the lower nibble
+ * of the last non-zero data
+ */
+ static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+ 12, 10, 8, 6, 4, 2, 0};
+
+ /*
+ * the lower nibble of the salt. Interleaved sequence.
+ * this is walked backwards according to len[]
+ */
+ static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+ 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+ /* circular list for the salt's hi nibble. */
+ static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+ 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+ /*
+ * start offset for upper nibble mapping.
+ * note: 9 is /special/. In the case where the high nibble == 0x9,
+ * hir[] is not used and - coincidentally - the salt's hi nibble is
+ * 0x09 regardless of the offset.
+ */
+ static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+ 3, 0x00, 14, 13, 8, 9, 10, 2};
+
+ const u8 ln = idx & 0xf;
+ const u8 hn = (idx >> 4) & 0xf;
+ const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+ if (len[ln] < off)
+ return 0x00;
+
+ return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+ u8 * const data = (u8 *) buffer;
+
+ if (data[MAGIC_DOT_BYTE] != 0x00) {
+ state->off = 0;
+ state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+ }
+ data[MAGIC_DOT_BYTE] ^= state->carry;
+ state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_dot *p = s->protocol;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ /*
+ * A first data channel is for MIDI conformant data channel, the rest is
+ * Multi Bit Linear Audio data channel.
+ */
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
+ if (err < 0)
+ return err;
+
+ s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+
+ if (s->direction == AMDTP_IN_STREAM)
+ p->midi_ports = DOT_MIDI_IN_PORTS;
+ else
+ p->midi_ports = DOT_MIDI_OUT_PORTS;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src << 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ buffer++;
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_dot *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0)
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+ unsigned int port, unsigned int count)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[0];
+
+ len = 0;
+ if (port < p->midi_ports &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL)
+ len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+ if (len > 0) {
+ b[3] = (0x10 << port) | len;
+ midi_use_bytes(s, port, len);
+ } else {
+ b[1] = 0;
+ b[2] = 0;
+ b[3] = 0;
+ }
+ b[0] = 0x80;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port, len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ b = (u8 *)&buffer[0];
+ port = b[3] >> 4;
+ len = b[3] & 0x0f;
+
+ if (port < p->midi_ports && p->midi[port] && len > 0)
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* This protocol delivers 24 bit data in 32bit data channel. */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (port < p->midi_ports)
+ ACCESS_ONCE(p->midi[port]) = midi;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ pcm_frames = 0;
+ }
+
+ read_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ write_pcm_silence(s, buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ write_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ enum cip_flags flags;
+
+ /* Use different mode between incoming/outgoing. */
+ if (dir == AMDTP_IN_STREAM) {
+ flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+ process_data_blocks = process_tx_data_blocks;
+ } else {
+ flags = CIP_BLOCKING;
+ process_data_blocks = process_rx_data_blocks;
+ }
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_data_blocks, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->state.carry = 0x00;
+ p->state.idx = 0x00;
+ p->state.off = 0;
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x-hwdep.c b/kernel/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644
index 000000000..f188e4758
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x-hwdep.c
@@ -0,0 +1,200 @@
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dg00x->lock);
+
+ while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+ prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dg00x->lock);
+ schedule();
+ finish_wait(&dg00x->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dg00x->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dg00x->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (dg00x->dev_lock_count > 0);
+ dg00x->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.digi00x_message.type =
+ SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+ event.digi00x_message.message = dg00x->msg;
+ dg00x->msg = 0;
+
+ count = min_t(long, count, sizeof(event.digi00x_message));
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &dg00x->hwdep_wait, wait);
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_changed || dg00x->msg)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dg00x->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == 0) {
+ dg00x->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == -1) {
+ dg00x->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_count == -1)
+ dg00x->dev_lock_count = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dg00x, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dg00x);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dg00x);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Digi00x");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = dg00x;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x-midi.c b/kernel/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644
index 000000000..1a72a382b
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x-midi.c
@@ -0,0 +1,223 @@
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int midi_phys_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ err = snd_dg00x_stream_start_duplex(dg00x, 0);
+ mutex_unlock(&dg00x->mutex);
+ if (err < 0)
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return err;
+}
+
+static int midi_phys_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter--;
+ snd_dg00x_stream_stop_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+
+ snd_dg00x_stream_lock_release(dg00x);
+ return 0;
+}
+
+static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
+ substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
+ NULL);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
+ substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
+ NULL);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_phys_capture_ops = {
+ .open = midi_phys_open,
+ .close = midi_phys_close,
+ .trigger = midi_phys_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_phys_playback_ops = {
+ .open = midi_phys_open,
+ .close = midi_phys_close,
+ .trigger = midi_phys_playback_trigger,
+};
+
+static int midi_ctl_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&dg00x->out_control);
+
+ return 0;
+}
+
+static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ dg00x->in_control = substream;
+ else
+ dg00x->in_control = NULL;
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ snd_fw_async_midi_port_run(&dg00x->out_control, substream);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_ctl_capture_ops = {
+ .open = midi_ctl_open,
+ .close = midi_ctl_capture_close,
+ .trigger = midi_ctl_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_ctl_playback_ops = {
+ .open = midi_ctl_open,
+ .close = midi_ctl_playback_close,
+ .trigger = midi_ctl_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dg00x *dg00x,
+ struct snd_rawmidi_str *str,
+ bool is_ctl)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ if (!is_ctl)
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ dg00x->card->shortname, subs->number + 1);
+ else
+ /* This port is for asynchronous transaction. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
+ }
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+ struct snd_rawmidi *rmidi[2];
+ struct snd_rawmidi_str *str;
+ unsigned int i;
+ int err;
+
+ /* Add physical midi ports. */
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
+ DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
+ "%s MIDI", dg00x->card->shortname);
+
+ snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_phys_capture_ops);
+ snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_phys_playback_ops);
+
+ /* Add a pair of control midi ports. */
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
+ 1, 1, &rmidi[1]);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
+ "%s control", dg00x->card->shortname);
+
+ snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_ctl_capture_ops);
+ snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_ctl_playback_ops);
+
+ for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
+ rmidi[i]->private_data = dg00x;
+
+ rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(dg00x, str, i);
+
+ rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(dg00x, str, i);
+
+ rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+ }
+
+ return 0;
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x-pcm.c b/kernel/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644
index 000000000..cac28f70a
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x-pcm.c
@@ -0,0 +1,373 @@
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(c,
+ snd_dg00x_stream_pcm_channels[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_rates[i]);
+ t.max = max(t.max, snd_dg00x_stream_rates[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+ t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 18,
+ .period_bytes_min = 4 * 18,
+ .period_bytes_max = 4 * 18 * 2048,
+ .buffer_bytes_max = 4 * 18 * 2048 * 2,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct amdtp_stream *s;
+ int err;
+
+ substream->runtime->hw = hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->tx_stream;
+ } else {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->rx_stream;
+ }
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ enum snd_dg00x_clock clock;
+ bool detect;
+ unsigned int rate;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(dg00x, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /* Check current clock source. */
+ err = snd_dg00x_stream_get_clock(dg00x, &clock);
+ if (err < 0)
+ goto err_locked;
+ if (clock != SND_DG00X_CLOCK_INTERNAL) {
+ err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+ if (err < 0)
+ goto err_locked;
+ if (!detect) {
+ err = -EBUSY;
+ goto err_locked;
+ }
+ }
+
+ if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+ amdtp_stream_pcm_running(&dg00x->rx_stream) ||
+ amdtp_stream_pcm_running(&dg00x->tx_stream)) {
+ err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_dg00x_stream_lock_release(dg00x);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dg00x->substreams_counter--;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dg00x->substreams_counter--;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ if (err >= 0) {
+ amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+ amdtp_dot_reset(&dg00x->rx_stream);
+ }
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = dg00x;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", dg00x->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x-proc.c b/kernel/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644
index 000000000..a1d601f31
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x-proc.c
@@ -0,0 +1,99 @@
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+ enum snd_dg00x_optical_mode *mode)
+{
+ __be32 data;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+ &data, sizeof(data), 0);
+ if (err >= 0)
+ *mode = be32_to_cpu(data) & 0x01;
+
+ return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buf)
+{
+ static const char *const source_name[] = {
+ [SND_DG00X_CLOCK_INTERNAL] = "internal",
+ [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+ [SND_DG00X_CLOCK_ADAT] = "adat",
+ [SND_DG00X_CLOCK_WORD] = "word clock",
+ };
+ static const char *const optical_name[] = {
+ [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+ [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+ };
+ struct snd_dg00x *dg00x = entry->private_data;
+ enum snd_dg00x_optical_mode mode;
+ unsigned int rate;
+ enum snd_dg00x_clock clock;
+ bool detect;
+
+ if (get_optical_iface_mode(dg00x, &mode) < 0)
+ return;
+ if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+ return;
+ if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+ return;
+
+ snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+ snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+ snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+ if (clock == SND_DG00X_CLOCK_INTERNAL)
+ return;
+
+ if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+ return;
+ snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+ if (!detect)
+ return;
+
+ if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+ snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+ struct snd_info_entry *root, *entry;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dg00x->card, "firewire",
+ dg00x->card->proc_root);
+ if (root == NULL)
+ return;
+
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+ if (entry == NULL) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ snd_info_free_entry(root);
+ }
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x-stream.c b/kernel/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644
index 000000000..4d3b4ebbd
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x-stream.c
@@ -0,0 +1,422 @@
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+#define CALLBACK_TIMEOUT 500
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+ [SND_DG00X_RATE_44100] = 44100,
+ [SND_DG00X_RATE_48000] = 48000,
+ [SND_DG00X_RATE_88200] = 88200,
+ [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+ /* Analog/ADAT/SPDIF */
+ [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+ [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+ /* Analog/SPDIF */
+ [SND_DG00X_RATE_88200] = (8 + 2),
+ [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ else
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+ if (rate == snd_dg00x_stream_rates[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+ return -EINVAL;
+
+ reg = cpu_to_be32(i);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *clock = be32_to_cpu(reg) & 0x0f;
+ if (*clock >= SND_DG00X_CLOCK_COUNT)
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *detect = be32_to_cpu(reg) > 0;
+
+ return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ /* This means desync. */
+ else
+ err = -EBUSY;
+
+ return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+ __be32 data = cpu_to_be32(0x00000003);
+
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+ u32 curr;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+ curr = be32_to_cpu(data);
+
+ if (curr == 0)
+ curr = 2;
+
+ curr--;
+ while (curr > 0) {
+ data = cpu_to_be32(curr);
+ err = snd_fw_transaction(dg00x->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE +
+ DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+
+ msleep(20);
+ curr--;
+ }
+
+ return 0;
+error:
+ finish_session(dg00x);
+ return err;
+}
+
+static void release_resources(struct snd_dg00x *dg00x)
+{
+ __be32 data = 0;
+
+ /* Unregister isochronous channels for both direction. */
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+
+ /* Release isochronous resources. */
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ unsigned int i;
+ __be32 data;
+ int err;
+
+ /* Check sampling rate. */
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (snd_dg00x_stream_rates[i] == rate)
+ break;
+ }
+ if (i == SND_DG00X_RATE_COUNT)
+ return -EINVAL;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&dg00x->rx_resources,
+ amdtp_stream_get_max_payload(&dg00x->rx_stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&dg00x->tx_resources,
+ amdtp_stream_get_max_payload(&dg00x->tx_stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ /* Register isochronous channels for both direction. */
+ data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+ dg00x->rx_resources.channel);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ release_resources(dg00x);
+ return err;
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ /* For out-stream. */
+ err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+ if (err < 0)
+ goto error;
+ err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
+ if (err < 0)
+ goto error;
+
+ /* For in-stream. */
+ err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+ if (err < 0)
+ goto error;
+ err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+ amdtp_stream_destroy(&dg00x->rx_stream);
+ fw_iso_resources_destroy(&dg00x->rx_resources);
+
+ amdtp_stream_destroy(&dg00x->tx_stream);
+ fw_iso_resources_destroy(&dg00x->tx_resources);
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err = 0;
+
+ if (dg00x->substreams_counter == 0)
+ goto end;
+
+ /* Check current sampling rate. */
+ err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+ if (err < 0)
+ goto error;
+ if (rate == 0)
+ rate = curr_rate;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&dg00x->tx_stream) ||
+ amdtp_streaming_error(&dg00x->rx_stream)) {
+ finish_session(dg00x);
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ release_resources(dg00x);
+ }
+
+ /*
+ * No packets are transmitted without receiving packets, reagardless of
+ * which source of clock is used.
+ */
+ if (!amdtp_stream_running(&dg00x->rx_stream)) {
+ err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+ if (err < 0)
+ goto error;
+
+ err = keep_resources(dg00x, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&dg00x->rx_stream,
+ dg00x->rx_resources.channel,
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ /*
+ * The value of SYT field in transmitted packets is always 0x0000. Thus,
+ * duplex streams with timestamp synchronization cannot be built.
+ */
+ if (!amdtp_stream_running(&dg00x->tx_stream)) {
+ err = amdtp_stream_start(&dg00x->tx_stream,
+ dg00x->tx_resources.channel,
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+end:
+ return err;
+error:
+ finish_session(dg00x);
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ release_resources(dg00x);
+
+ return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+ if (dg00x->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ finish_session(dg00x);
+ release_resources(dg00x);
+
+ /*
+ * Just after finishing the session, the device may lost transmitting
+ * functionality for a short time.
+ */
+ msleep(50);
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+ fw_iso_resources_update(&dg00x->tx_resources);
+ fw_iso_resources_update(&dg00x->rx_resources);
+
+ amdtp_stream_update(&dg00x->tx_stream);
+ amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+ dg00x->dev_lock_changed = true;
+ wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ /* user land lock this */
+ if (dg00x->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (dg00x->dev_lock_count++ == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+ err = 0;
+end:
+ spin_unlock_irq(&dg00x->lock);
+ return err;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+ spin_lock_irq(&dg00x->lock);
+
+ if (WARN_ON(dg00x->dev_lock_count <= 0))
+ goto end;
+ if (--dg00x->dev_lock_count == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+end:
+ spin_unlock_irq(&dg00x->lock);
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x-transaction.c b/kernel/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644
index 000000000..554324d8c
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x-transaction.c
@@ -0,0 +1,137 @@
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+ int bytes;
+
+ buf[0] = 0x80;
+ bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
+ if (bytes >= 0)
+ buf[3] = 0xc0 | bytes;
+
+ return bytes;
+}
+
+static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
+ unsigned int length)
+{
+ struct snd_rawmidi_substream *substream;
+ unsigned int i;
+ unsigned int len;
+ u8 *b;
+
+ substream = ACCESS_ONCE(dg00x->in_control);
+ if (substream == NULL)
+ return;
+
+ length /= 4;
+
+ for (i = 0; i < length; i++) {
+ b = (u8 *)&buf[i];
+ len = b[3] & 0xf;
+ if (len > 0)
+ snd_rawmidi_receive(dg00x->in_control, b + 1, len);
+ }
+}
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+ unsigned long long offset, __be32 *buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+ dg00x->msg = be32_to_cpu(*buf);
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+
+ wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dg00x *dg00x = callback_data;
+ __be32 *buf = (__be32 *)data;
+
+ if (offset == dg00x->async_handler.offset)
+ handle_unknown_message(dg00x, offset, buf);
+ else if (offset == dg00x->async_handler.offset + 4)
+ handle_midi_control(dg00x, buf, length);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+ struct fw_device *device = fw_parent_device(dg00x->unit);
+ __be32 data[2];
+ int err;
+
+ /* Unknown. 4bytes. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+
+ /* Asynchronous transactions for MIDI control message. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
+ &data, sizeof(data), 0);
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ dg00x->async_handler.length = 12;
+ dg00x->async_handler.address_callback = handle_message;
+ dg00x->async_handler.callback_data = dg00x;
+
+ err = fw_core_add_address_handler(&dg00x->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_dg00x_transaction_reregister(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
+ 4, fill_midi_message);
+ if (err < 0)
+ goto error;
+
+ return err;
+error:
+ fw_core_remove_address_handler(&dg00x->async_handler);
+ dg00x->async_handler.address_callback = NULL;
+ return err;
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+ snd_fw_async_midi_port_destroy(&dg00x->out_control);
+ fw_core_remove_address_handler(&dg00x->async_handler);
+}
diff --git a/kernel/sound/firewire/digi00x/digi00x.c b/kernel/sound/firewire/digi00x/digi00x.c
new file mode 100644
index 000000000..1f33b7a1f
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x.c
@@ -0,0 +1,170 @@
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+#define VENDOR_DIGIDESIGN 0x00a07e
+#define MODEL_DIGI00X 0x000002
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+ struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+ char name[32] = {0};
+ char *model;
+ int err;
+
+ err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+ sizeof(name));
+ if (err < 0)
+ return err;
+
+ model = skip_spaces(name);
+
+ strcpy(dg00x->card->driver, "Digi00x");
+ strcpy(dg00x->card->shortname, model);
+ strcpy(dg00x->card->mixername, model);
+ snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+ "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+ struct snd_dg00x *dg00x = card->private_data;
+
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_dg00x_transaction_unregister(dg00x);
+
+ fw_unit_put(dg00x->unit);
+
+ mutex_destroy(&dg00x->mutex);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dg00x *dg00x;
+ int err;
+
+ /* create card */
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(struct snd_dg00x), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dg00x_card_free;
+
+ /* initialize myself */
+ dg00x = card->private_data;
+ dg00x->card = card;
+ dg00x->unit = fw_unit_get(unit);
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ err = name_card(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_stream_init_duplex(dg00x);
+ if (err < 0)
+ goto error;
+
+ snd_dg00x_proc_init(dg00x);
+
+ err = snd_dg00x_create_pcm_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_midi_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_hwdep_device(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_transaction_register(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, dg00x);
+
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ snd_dg00x_transaction_reregister(dg00x);
+
+ mutex_lock(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+ /* Both of 002/003 use the same ID. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .model_id = MODEL_DIGI00X,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-firewire-digi00x",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_dg00x_probe,
+ .update = snd_dg00x_update,
+ .remove = snd_dg00x_remove,
+ .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+ return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+ driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/kernel/sound/firewire/digi00x/digi00x.h b/kernel/sound/firewire/digi00x/digi00x.h
new file mode 100644
index 000000000..907e73993
--- /dev/null
+++ b/kernel/sound/firewire/digi00x/digi00x.h
@@ -0,0 +1,157 @@
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ struct amdtp_stream tx_stream;
+ struct fw_iso_resources tx_resources;
+
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources rx_resources;
+
+ unsigned int substreams_counter;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For asynchronous messages. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* For asynchronous MIDI controls. */
+ struct snd_rawmidi_substream *in_control;
+ struct snd_fw_async_midi_port out_control;
+};
+
+#define DG00X_ADDR_BASE 0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE 0x0000
+#define DG00X_OFFSET_STREAMING_SET 0x0004
+#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008
+/* For LSB of the address 0x000c */
+/* unknown 0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
+/* For LSB of the address 0x0018 */
+/* unknown 0x001c */
+/* unknown 0x0020 */
+/* not used 0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS 0x0100
+/* unknown 0x0104 */
+/* unknown 0x0108 */
+/* unknown 0x010c */
+#define DG00X_OFFSET_LOCAL_RATE 0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE 0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE 0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c
+/* unknown 0x0120 */
+/* Mixer control on/off 0x0124 */
+/* unknown 0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c
+/* unknown 0x0138 */
+#define DG00X_OFFSET_MMC 0x0400
+
+enum snd_dg00x_rate {
+ SND_DG00X_RATE_44100 = 0,
+ SND_DG00X_RATE_48000,
+ SND_DG00X_RATE_88200,
+ SND_DG00X_RATE_96000,
+ SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+ SND_DG00X_CLOCK_INTERNAL = 0,
+ SND_DG00X_CLOCK_SPDIF,
+ SND_DG00X_CLOCK_ADAT,
+ SND_DG00X_CLOCK_WORD,
+ SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+ SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+ SND_DG00X_OPT_IFACE_MODE_SPDIF,
+ SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS 1
+#define DOT_MIDI_OUT_PORTS 2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+ bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
diff --git a/kernel/sound/firewire/fcp.c b/kernel/sound/firewire/fcp.c
index 0619597e3..cce19768f 100644
--- a/kernel/sound/firewire/fcp.c
+++ b/kernel/sound/firewire/fcp.c
@@ -17,7 +17,7 @@
#include <linux/delay.h>
#include "fcp.h"
#include "lib.h"
-#include "amdtp.h"
+#include "amdtp-stream.h"
#define CTS_AVC 0x00
diff --git a/kernel/sound/firewire/fireworks/Makefile b/kernel/sound/firewire/fireworks/Makefile
index 0c7440826..15ef7f75a 100644
--- a/kernel/sound/firewire/fireworks/Makefile
+++ b/kernel/sound/firewire/fireworks/Makefile
@@ -1,4 +1,4 @@
snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
fireworks_stream.o fireworks_proc.o fireworks_midi.o \
fireworks_pcm.o fireworks_hwdep.o fireworks.o
-obj-m += snd-fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
diff --git a/kernel/sound/firewire/fireworks/fireworks.c b/kernel/sound/firewire/fireworks/fireworks.c
index c94a432f7..d5b19bc11 100644
--- a/kernel/sound/firewire/fireworks/fireworks.c
+++ b/kernel/sound/firewire/fireworks/fireworks.c
@@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw)
efw->midi_out_ports = hwinfo->midi_out_ports;
efw->midi_in_ports = hwinfo->midi_in_ports;
- if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+ if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
diff --git a/kernel/sound/firewire/fireworks/fireworks.h b/kernel/sound/firewire/fireworks/fireworks.h
index 084d414b2..c7cb7deaf 100644
--- a/kernel/sound/firewire/fireworks/fireworks.h
+++ b/kernel/sound/firewire/fireworks/fireworks.h
@@ -29,7 +29,7 @@
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
#include "../lib.h"
diff --git a/kernel/sound/firewire/fireworks/fireworks_command.c b/kernel/sound/firewire/fireworks/fireworks_command.c
index 166f80584..94bab0476 100644
--- a/kernel/sound/firewire/fireworks/fireworks_command.c
+++ b/kernel/sound/firewire/fireworks/fireworks_command.c
@@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw,
struct snd_efw_phys_meters *meters,
unsigned int len)
{
- __be32 *buf = (__be32 *)meters;
+ u32 *buf = (u32 *)meters;
unsigned int i;
int err;
diff --git a/kernel/sound/firewire/fireworks/fireworks_midi.c b/kernel/sound/firewire/fireworks/fireworks_midi.c
index cf9c65260..fba01bbba 100644
--- a/kernel/sound/firewire/fireworks/fireworks_midi.c
+++ b/kernel/sound/firewire/fireworks/fireworks_midi.c
@@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&efw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&efw->tx_stream,
+ amdtp_am824_midi_trigger(&efw->tx_stream,
substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&efw->tx_stream,
+ amdtp_am824_midi_trigger(&efw->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
@@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&efw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&efw->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&efw->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
}
diff --git a/kernel/sound/firewire/fireworks/fireworks_pcm.c b/kernel/sound/firewire/fireworks/fireworks_pcm.c
index 8a34753de..d27135bac 100644
--- a/kernel/sound/firewire/fireworks/fireworks_pcm.c
+++ b/kernel/sound/firewire/fireworks/fireworks_pcm.c
@@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
s = &efw->tx_stream;
pcm_channels = efw->pcm_capture_channels;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
s = &efw->rx_stream;
pcm_channels = efw->pcm_playback_channels;
}
@@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
@@ -244,25 +244,37 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_efw *efw = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->capture_substreams);
- amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+ return 0;
}
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_efw *efw = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->playback_substreams);
- amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+ return 0;
}
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
diff --git a/kernel/sound/firewire/fireworks/fireworks_stream.c b/kernel/sound/firewire/fireworks/fireworks_stream.c
index 7e353f1f7..759f6e3ed 100644
--- a/kernel/sound/firewire/fireworks/fireworks_stream.c
+++ b/kernel/sound/firewire/fireworks/fireworks_stream.c
@@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
if (err < 0)
goto end;
- err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
@@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
midi_ports = efw->midi_in_ports;
}
- amdtp_stream_set_parameters(stream, sampling_rate,
- pcm_channels, midi_ports);
+ err = amdtp_am824_set_parameters(stream, sampling_rate,
+ pcm_channels, midi_ports, false);
+ if (err < 0)
+ goto end;
/* establish connection via CMP */
err = cmp_connection_establish(conn,
diff --git a/kernel/sound/firewire/lib.c b/kernel/sound/firewire/lib.c
index 7409edba9..f80aafa44 100644
--- a/kernel/sound/firewire/lib.c
+++ b/kernel/sound/firewire/lib.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "lib.h"
#define ERROR_RETRY_DELAY_MS 20
@@ -66,6 +67,147 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
EXPORT_SYMBOL(snd_fw_transaction);
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_fw_async_midi_port *port = callback_data;
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+
+ /* This port is closed. */
+ if (substream == NULL)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+ else if (!rcode_is_permanent_error(rcode))
+ /* To start next transaction immediately for recovery. */
+ port->next_ktime = ktime_set(0, 0);
+ else
+ /* Don't continue processing. */
+ port->error = true;
+
+ port->idling = true;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+ struct snd_fw_async_midi_port *port =
+ container_of(work, struct snd_fw_async_midi_port, work);
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+ int generation;
+ int type;
+
+ /* Under transacting or error state. */
+ if (!port->idling || port->error)
+ return;
+
+ /* Nothing to do. */
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
+ /*
+ * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+ * Later, snd_rawmidi_transmit_ack() is called.
+ */
+ memset(port->buf, 0, port->len);
+ port->consume_bytes = port->fill(substream, port->buf);
+ if (port->consume_bytes <= 0) {
+ /* Do it in next chance, immediately. */
+ if (port->consume_bytes == 0) {
+ port->next_ktime = ktime_set(0, 0);
+ schedule_work(&port->work);
+ } else {
+ /* Fatal error. */
+ port->error = true;
+ }
+ return;
+ }
+
+ /* Calculate type of transaction. */
+ if (port->len == 4)
+ type = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ type = TCODE_WRITE_BLOCK_REQUEST;
+
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
+ /* Start this transaction. */
+ port->idling = false;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = port->parent->generation;
+ smp_rmb();
+
+ fw_send_request(port->parent->card, &port->transaction, type,
+ port->parent->node_id, generation,
+ port->parent->max_speed, port->addr,
+ port->buf, port->len, async_midi_port_callback,
+ port);
+}
+
+/**
+ * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port to initialize
+ * @unit: the target of the asynchronous transaction
+ * @addr: the address to which transactions are transferred
+ * @len: the length of transaction
+ * @fill: the callback function to fill given buffer, and returns the
+ * number of consumed bytes for MIDI message.
+ *
+ */
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+ struct fw_unit *unit, u64 addr, unsigned int len,
+ snd_fw_async_midi_port_fill fill)
+{
+ port->len = DIV_ROUND_UP(len, 4) * 4;
+ port->buf = kzalloc(port->len, GFP_KERNEL);
+ if (port->buf == NULL)
+ return -ENOMEM;
+
+ port->parent = fw_parent_device(unit);
+ port->addr = addr;
+ port->fill = fill;
+ port->idling = true;
+ port->next_ktime = ktime_set(0, 0);
+ port->error = false;
+
+ INIT_WORK(&port->work, midi_port_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_init);
+
+/**
+ * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port structure
+ */
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
+{
+ snd_fw_async_midi_port_finish(port);
+ cancel_work_sync(&port->work);
+ kfree(port->buf);
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
+
MODULE_DESCRIPTION("FireWire audio helper functions");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/firewire/lib.h b/kernel/sound/firewire/lib.h
index 02cfabc9c..f3f6f84c4 100644
--- a/kernel/sound/firewire/lib.h
+++ b/kernel/sound/firewire/lib.h
@@ -3,6 +3,8 @@
#include <linux/firewire-constants.h>
#include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
struct fw_unit;
@@ -20,4 +22,58 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
}
+struct snd_fw_async_midi_port;
+typedef int (*snd_fw_async_midi_port_fill)(
+ struct snd_rawmidi_substream *substream,
+ u8 *buf);
+
+struct snd_fw_async_midi_port {
+ struct fw_device *parent;
+ struct work_struct work;
+ bool idling;
+ ktime_t next_ktime;
+ bool error;
+
+ u64 addr;
+ struct fw_transaction transaction;
+
+ u8 *buf;
+ unsigned int len;
+
+ struct snd_rawmidi_substream *substream;
+ snd_fw_async_midi_port_fill fill;
+ unsigned int consume_bytes;
+};
+
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+ struct fw_unit *unit, u64 addr, unsigned int len,
+ snd_fw_async_midi_port_fill fill);
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
+
+/**
+ * snd_fw_async_midi_port_run - run transactions for the async MIDI port
+ * @port: the asynchronous MIDI port
+ * @substream: the MIDI substream
+ */
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ if (!port->error) {
+ port->substream = substream;
+ schedule_work(&port->work);
+ }
+}
+
+/**
+ * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
+ * @port: the asynchronous MIDI port
+ */
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+ port->substream = NULL;
+ port->error = false;
+}
+
#endif
diff --git a/kernel/sound/firewire/oxfw/Makefile b/kernel/sound/firewire/oxfw/Makefile
index a92685086..06ff50f4e 100644
--- a/kernel/sound/firewire/oxfw/Makefile
+++ b/kernel/sound/firewire/oxfw/Makefile
@@ -1,3 +1,3 @@
snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
-obj-m += snd-oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/kernel/sound/firewire/oxfw/oxfw-midi.c b/kernel/sound/firewire/oxfw/oxfw-midi.c
index 540a30338..8665e1043 100644
--- a/kernel/sound/firewire/oxfw/oxfw-midi.c
+++ b/kernel/sound/firewire/oxfw/oxfw-midi.c
@@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&oxfw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&oxfw->tx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&oxfw->tx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&oxfw->lock, flags);
}
@@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&oxfw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&oxfw->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&oxfw->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&oxfw->lock, flags);
}
@@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
{
- struct snd_oxfw_stream_formation formation;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
- u8 *format;
- int i, err;
-
- /* If its stream has MIDI conformant data channel, add one MIDI port */
- for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
- format = oxfw->tx_stream_formats[i];
- if (format != NULL) {
- err = snd_oxfw_stream_parse_format(format, &formation);
- if (err >= 0 && formation.midi > 0)
- oxfw->midi_input_ports = 1;
- }
-
- format = oxfw->rx_stream_formats[i];
- if (format != NULL) {
- err = snd_oxfw_stream_parse_format(format, &formation);
- if (err >= 0 && formation.midi > 0)
- oxfw->midi_output_ports = 1;
- }
- }
- if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+ int err;
+
+ if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
return 0;
/* create midi ports */
diff --git a/kernel/sound/firewire/oxfw/oxfw-pcm.c b/kernel/sound/firewire/oxfw/oxfw-pcm.c
index 67ade0775..8d2334176 100644
--- a/kernel/sound/firewire/oxfw/oxfw-pcm.c
+++ b/kernel/sound/firewire/oxfw/oxfw-pcm.c
@@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
stream = &oxfw->tx_stream;
formats = oxfw->tx_stream_formats;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &oxfw->rx_stream;
formats = oxfw->rx_stream_formats;
}
@@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
}
@@ -231,7 +231,12 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&oxfw->mutex);
@@ -239,15 +244,20 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&oxfw->mutex);
@@ -255,10 +265,9 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
diff --git a/kernel/sound/firewire/oxfw/oxfw-stream.c b/kernel/sound/firewire/oxfw/oxfw-stream.c
index e6757cd85..7cb5743c0 100644
--- a/kernel/sound/firewire/oxfw/oxfw-stream.c
+++ b/kernel/sound/firewire/oxfw/oxfw-stream.c
@@ -148,14 +148,17 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
}
pcm_channels = formation.pcm;
- midi_ports = DIV_ROUND_UP(formation.midi, 8);
+ midi_ports = formation.midi * 8;
/* The stream should have one pcm channels at least */
if (pcm_channels == 0) {
err = -EINVAL;
goto end;
}
- amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
+ false);
+ if (err < 0)
+ goto end;
err = cmp_connection_establish(conn,
amdtp_stream_get_max_payload(stream));
@@ -225,16 +228,25 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
if (err < 0)
goto end;
- err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
goto end;
}
- /* OXFW starts to transmit packets with non-zero dbc. */
- if (stream == &oxfw->tx_stream)
- oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+ /*
+ * 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 |
+ CIP_JUMBO_PAYLOAD;
+ if (oxfw->wrong_dbs)
+ oxfw->tx_stream.flags |= CIP_WRONG_DBS;
+ }
end:
return err;
}
@@ -474,8 +486,8 @@ int snd_oxfw_stream_parse_format(u8 *format,
}
}
- if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
- formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
return -ENOSYS;
return 0;
@@ -506,12 +518,11 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
if (err < 0)
goto end;
- formats[eid] = kmalloc(*len, GFP_KERNEL);
+ formats[eid] = kmemdup(buf, *len, GFP_KERNEL);
if (formats[eid] == NULL) {
err = -ENOMEM;
goto end;
}
- memcpy(formats[eid], buf, *len);
/* apply the format for each available sampling rate */
for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
@@ -525,12 +536,11 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
continue;
eid++;
- formats[eid] = kmalloc(*len, GFP_KERNEL);
+ formats[eid] = kmemdup(buf, *len, GFP_KERNEL);
if (formats[eid] == NULL) {
err = -ENOMEM;
goto end;
}
- memcpy(formats[eid], buf, *len);
formats[eid][2] = avc_stream_rate_table[i];
}
@@ -588,12 +598,11 @@ static int fill_stream_formats(struct snd_oxfw *oxfw,
if (err < 0)
break;
- formats[eid] = kmalloc(len, GFP_KERNEL);
+ formats[eid] = kmemdup(buf, len, GFP_KERNEL);
if (formats[eid] == NULL) {
err = -ENOMEM;
break;
}
- memcpy(formats[eid], buf, len);
/* get next entry */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
@@ -620,6 +629,9 @@ end:
int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
{
u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+ struct snd_oxfw_stream_formation formation;
+ u8 *format;
+ unsigned int i;
int err;
/* the number of plugs for isoc in/out, ext in/out */
@@ -639,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
if (err < 0)
goto end;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_input_ports = 1;
+ }
+
oxfw->has_output = true;
}
/* use iPCR[0] if exists */
- if (plugs[0] > 0)
+ if (plugs[0] > 0) {
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_output_ports = 1;
+ }
+ }
end:
return err;
}
diff --git a/kernel/sound/firewire/oxfw/oxfw.c b/kernel/sound/firewire/oxfw/oxfw.c
index 8c6ce019f..588b93f20 100644
--- a/kernel/sound/firewire/oxfw/oxfw.c
+++ b/kernel/sound/firewire/oxfw/oxfw.c
@@ -18,6 +18,9 @@
#define VENDOR_GRIFFIN 0x001292
#define VENDOR_BEHRINGER 0x001564
#define VENDOR_LACIE 0x00d04b
+#define VENDOR_TASCAM 0x00022e
+
+#define MODEL_SATELLITE 0x00200f
#define SPECIFIER_1394TA 0x00a02d
#define VERSION_AVC 0x010001
@@ -129,6 +132,40 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex);
}
+static void detect_quirks(struct snd_oxfw *oxfw)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ int vendor, model;
+
+ /* Seek from Root Directory of Config ROM. */
+ vendor = model = 0;
+ fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_VENDOR)
+ vendor = val;
+ else if (key == CSR_MODEL)
+ model = val;
+ }
+
+ /*
+ * Mackie Onyx Satellite with base station has a quirk to report a wrong
+ * value in 'dbs' field of CIP header against its format information.
+ */
+ if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
+ oxfw->wrong_dbs = true;
+
+ /*
+ * TASCAM FireOne has physical control and requires a pair of additional
+ * MIDI ports.
+ */
+ if (vendor == VENDOR_TASCAM) {
+ oxfw->midi_input_ports++;
+ oxfw->midi_output_ports++;
+ }
+}
+
static int oxfw_probe(struct fw_unit *unit,
const struct ieee1394_device_id *id)
{
@@ -157,6 +194,8 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
+ detect_quirks(oxfw);
+
err = name_card(oxfw);
if (err < 0)
goto error;
@@ -294,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
.specifier_id = SPECIFIER_1394TA,
.version = VERSION_AVC,
},
+ /* TASCAM, FireOne */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_TASCAM,
+ .model_id = 0x800007,
+ },
{ }
};
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
diff --git a/kernel/sound/firewire/oxfw/oxfw.h b/kernel/sound/firewire/oxfw/oxfw.h
index cace5ad4f..8392c424a 100644
--- a/kernel/sound/firewire/oxfw/oxfw.h
+++ b/kernel/sound/firewire/oxfw/oxfw.h
@@ -28,7 +28,7 @@
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
struct device_info {
@@ -49,6 +49,7 @@ struct snd_oxfw {
struct mutex mutex;
spinlock_t lock;
+ bool wrong_dbs;
bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/kernel/sound/firewire/tascam/Makefile b/kernel/sound/firewire/tascam/Makefile
new file mode 100644
index 000000000..0fc955d5b
--- /dev/null
+++ b/kernel/sound/firewire/tascam/Makefile
@@ -0,0 +1,4 @@
+snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+ tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/kernel/sound/firewire/tascam/amdtp-tascam.c b/kernel/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644
index 000000000..9dd0fccd5
--- /dev/null
+++ b/kernel/sound/firewire/tascam/amdtp-tascam.c
@@ -0,0 +1,243 @@
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX 0x1e
+#define AMDTP_FMT_TSCM_RX 0x3e
+
+struct amdtp_tscm {
+ unsigned int pcm_channels;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ data_channels = p->pcm_channels;
+
+ /* Packets in in-stream have extra 2 data channels. */
+ if (s->direction == AMDTP_IN_STREAM)
+ data_channels += 2;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src << 16);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ /* The first data channel is for event counter. */
+ buffer += 1;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]);
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = 0x00000000;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /*
+ * Our implementation allows this protocol to deliver 24 bit sample in
+ * 32bit data channel.
+ */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_tscm *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (data_blocks > 0 && pcm)
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+
+ /* A place holder for control messages. */
+
+ return data_blocks;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ /* This field is not used. */
+ *syt = 0x0000;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm)
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ else
+ write_pcm_silence(s, buffer, data_blocks);
+
+ return data_blocks;
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ struct amdtp_tscm *p;
+ unsigned int fmt;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ fmt = AMDTP_FMT_TSCM_TX;
+ process_data_blocks = process_tx_data_blocks;
+ } else {
+ fmt = AMDTP_FMT_TSCM_RX;
+ process_data_blocks = process_rx_data_blocks;
+ }
+
+ err = amdtp_stream_init(s, unit, dir,
+ CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
+ process_data_blocks, sizeof(struct amdtp_tscm));
+ if (err < 0)
+ return 0;
+
+ /* Use fixed value for FDF field. */
+ s->fdf = 0x00;
+
+ /* This protocol uses fixed number of data channels for PCM samples. */
+ p = s->protocol;
+ p->pcm_channels = pcm_channels;
+
+ return 0;
+}
diff --git a/kernel/sound/firewire/tascam/tascam-hwdep.c b/kernel/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644
index 000000000..131267c3a
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam-hwdep.c
@@ -0,0 +1,201 @@
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+ long count)
+{
+ union snd_firewire_event event;
+
+ memset(&event, 0, sizeof(event));
+
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (tscm->dev_lock_count > 0);
+ tscm->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&tscm->lock);
+
+ while (!tscm->dev_lock_changed) {
+ prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&tscm->lock);
+ schedule();
+ finish_wait(&tscm->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&tscm->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ count = hwdep_read_locked(tscm, buf, count);
+ spin_unlock_irq(&tscm->lock);
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &tscm->hwdep_wait, wait);
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_changed)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(tscm->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == 0) {
+ tscm->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == -1) {
+ tscm->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_count == -1)
+ tscm->dev_lock_count = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(tscm, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(tscm);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Tascam");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = tscm;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/kernel/sound/firewire/tascam/tascam-midi.c b/kernel/sound/firewire/tascam/tascam-midi.c
new file mode 100644
index 000000000..41f842079
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam-midi.c
@@ -0,0 +1,135 @@
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ /* Initialize internal status. */
+ tscm->running_status[substream->number] = 0;
+ tscm->on_sysex[substream->number] = 0;
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ tscm->tx_midi_substreams[substrm->number] = substrm;
+ else
+ tscm->tx_midi_substreams[substrm->number] = NULL;
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+ substrm);
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ struct snd_rawmidi_substream *subs;
+ int err;
+
+ err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+ tscm->spec->midi_playback_ports,
+ tscm->spec->midi_capture_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", tscm->card->shortname);
+ rmidi->private_data = tscm;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ /* Set port names for MIDI input. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ /* TODO: support virtual MIDI ports. */
+ if (subs->number < tscm->spec->midi_capture_ports) {
+ /* Hardware MIDI ports. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ /* Set port names for MIDI ourput. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ if (subs->number < tscm->spec->midi_playback_ports) {
+ /* Hardware MIDI ports only. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/kernel/sound/firewire/tascam/tascam-pcm.c b/kernel/sound/firewire/tascam/tascam-pcm.c
new file mode 100644
index 000000000..380d3db96
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam-pcm.c
@@ -0,0 +1,312 @@
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static void set_buffer_params(struct snd_pcm_hardware *hw)
+{
+ hw->period_bytes_min = 4 * hw->channels_min;
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * 2;
+
+ hw->periods_min = 2;
+ hw->periods_max = UINT_MAX;
+}
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 18,
+ };
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *stream;
+ unsigned int pcm_channels;
+
+ runtime->hw = hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->tx_stream;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ runtime->hw.formats =
+ SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->rx_stream;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+ set_buffer_params(&runtime->hw);
+
+ return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ enum snd_tscm_clock clock;
+ unsigned int rate;
+ int err;
+
+ err = snd_tscm_stream_lock_try(tscm);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(tscm, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_tscm_stream_get_clock(tscm, &clock);
+ if (clock != SND_TSCM_CLOCK_INTERNAL ||
+ amdtp_stream_pcm_running(&tscm->rx_stream) ||
+ amdtp_stream_pcm_running(&tscm->tx_stream)) {
+ err = snd_tscm_stream_get_rate(tscm, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_tscm_stream_lock_release(tscm);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ snd_tscm_stream_lock_release(tscm);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&tscm->mutex);
+ tscm->substreams_counter++;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&tscm->mutex);
+ tscm->substreams_counter++;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ tscm->substreams_counter--;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ tscm->substreams_counter--;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&tscm->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = tscm;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", tscm->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/kernel/sound/firewire/tascam/tascam-proc.c b/kernel/sound/firewire/tascam/tascam-proc.c
new file mode 100644
index 000000000..bfd4a4c06
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam-proc.c
@@ -0,0 +1,88 @@
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_tscm *tscm = entry->private_data;
+ __be32 data;
+ unsigned int reg, fpga, arm, hw;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ reg = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ fpga = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ arm = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ hw = be32_to_cpu(data);
+
+ snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+ snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga);
+ snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm);
+ snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(tscm->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, tscm, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(tscm->card, "firewire",
+ tscm->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/kernel/sound/firewire/tascam/tascam-stream.c b/kernel/sound/firewire/tascam/tascam-stream.c
new file mode 100644
index 000000000..0e6dd5c61
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam-stream.c
@@ -0,0 +1,496 @@
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CALLBACK_TIMEOUT 500
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *data = be32_to_cpu(reg);
+
+ return err;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+ enum snd_tscm_clock clock)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+ data &= 0x0000ffff;
+
+ if (rate > 0) {
+ data &= 0x000000ff;
+ /* Base rate. */
+ if ((rate % 44100) == 0) {
+ data |= 0x00000100;
+ /* Multiplier. */
+ if (rate / 44100 == 2)
+ data |= 0x00008000;
+ } else if ((rate % 48000) == 0) {
+ data |= 0x00000200;
+ /* Multiplier. */
+ if (rate / 48000 == 2)
+ data |= 0x00008000;
+ } else {
+ return -EAGAIN;
+ }
+ }
+
+ if (clock != INT_MAX) {
+ data &= 0x0000ff00;
+ data |= clock + 1;
+ }
+
+ reg = cpu_to_be32(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ if (data & 0x00008000)
+ reg = cpu_to_be32(0x0000001a);
+ else
+ reg = cpu_to_be32(0x0000000d);
+
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+ u32 data = 0x0;
+ unsigned int trials = 0;
+ int err;
+
+ while (data == 0x0 || trials++ < 5) {
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ data = (data & 0xff000000) >> 24;
+ }
+
+ /* Check base rate. */
+ if ((data & 0x0f) == 0x01)
+ *rate = 44100;
+ else if ((data & 0x0f) == 0x02)
+ *rate = 48000;
+ else
+ return -EAGAIN;
+
+ /* Check multiplier. */
+ if ((data & 0xf0) == 0x80)
+ *rate *= 2;
+ else if ((data & 0xf0) != 0x00)
+ return -EAGAIN;
+
+ return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ *clock = ((data & 0x00ff0000) >> 16) - 1;
+ if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+ return -EIO;
+
+ return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ /* Set an option for unknown purpose. */
+ reg = cpu_to_be32(0x00200000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ err = enable_data_channels(tscm);
+ if (err < 0)
+ return err;
+
+ return set_clock(tscm, rate, INT_MAX);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ int err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Set an option for unknown purpose. */
+ reg = cpu_to_be32(0x00002000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Start multiplexing PCM samples on packets. */
+ reg = cpu_to_be32(0x00000001);
+ return snd_fw_transaction(tscm->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+ &reg, sizeof(reg), 0);
+}
+
+static void release_resources(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ /* Unregister channels. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+
+ /* Release isochronous resources. */
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&tscm->tx_resources,
+ amdtp_stream_get_max_payload(&tscm->tx_stream),
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&tscm->rx_resources,
+ amdtp_stream_get_max_payload(&tscm->rx_stream),
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Register the isochronous channel for transmitting stream. */
+ reg = cpu_to_be32(tscm->tx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ /* Unknown */
+ reg = cpu_to_be32(0x00000002);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ /* Register the isochronous channel for receiving stream. */
+ reg = cpu_to_be32(tscm->rx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ release_resources(tscm);
+ return err;
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+ unsigned int pcm_channels;
+ int err;
+
+ /* For out-stream. */
+ err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
+ if (err < 0)
+ return err;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
+ pcm_channels);
+ if (err < 0)
+ return err;
+
+ /* For in-stream. */
+ err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
+ if (err < 0)
+ return err;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
+ pcm_channels);
+ if (err < 0)
+ amdtp_stream_destroy(&tscm->rx_stream);
+
+ return 0;
+}
+
+/* At bus reset, streaming is stopped and some registers are clear. */
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+ amdtp_stream_pcm_abort(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
+
+ amdtp_stream_pcm_abort(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+ amdtp_stream_destroy(&tscm->rx_stream);
+ amdtp_stream_destroy(&tscm->tx_stream);
+
+ fw_iso_resources_destroy(&tscm->rx_resources);
+ fw_iso_resources_destroy(&tscm->tx_resources);
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err;
+
+ if (tscm->substreams_counter == 0)
+ return 0;
+
+ err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+ if (err < 0)
+ return err;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&tscm->tx_stream) ||
+ amdtp_streaming_error(&tscm->rx_stream)) {
+ finish_session(tscm);
+
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ release_resources(tscm);
+ }
+
+ if (!amdtp_stream_running(&tscm->tx_stream)) {
+ amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
+ &tscm->tx_stream, &tscm->rx_stream);
+ err = keep_resources(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = set_stream_formats(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(tscm);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&tscm->tx_stream,
+ tscm->tx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
+ err = amdtp_stream_start(&tscm->rx_stream,
+ tscm->rx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ finish_session(tscm);
+ release_resources(tscm);
+
+ return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+ if (tscm->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ finish_session(tscm);
+ release_resources(tscm);
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+ tscm->dev_lock_changed = true;
+ wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ /* user land lock this */
+ if (tscm->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (tscm->dev_lock_count++ == 0)
+ snd_tscm_stream_lock_changed(tscm);
+ err = 0;
+end:
+ spin_unlock_irq(&tscm->lock);
+ return err;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+ spin_lock_irq(&tscm->lock);
+
+ if (WARN_ON(tscm->dev_lock_count <= 0))
+ goto end;
+ if (--tscm->dev_lock_count == 0)
+ snd_tscm_stream_lock_changed(tscm);
+end:
+ spin_unlock_irq(&tscm->lock);
+}
diff --git a/kernel/sound/firewire/tascam/tascam-transaction.c b/kernel/sound/firewire/tascam/tascam-transaction.c
new file mode 100644
index 000000000..904ce0329
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam-transaction.c
@@ -0,0 +1,302 @@
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+ unsigned int port = substream->number;
+ int i, len, consume;
+ u8 *label, *msg;
+ u8 status;
+
+ /* The first byte is used for label, the rest for MIDI bytes. */
+ label = buf;
+ msg = buf + 1;
+
+ consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+ if (consume == 0)
+ return 0;
+
+ /* On exclusive message. */
+ if (tscm->on_sysex[port]) {
+ /* Seek the end of exclusives. */
+ for (i = 0; i < consume; ++i) {
+ if (msg[i] == 0xf7) {
+ tscm->on_sysex[port] = false;
+ break;
+ }
+ }
+
+ /* At the end of exclusive message, use label 0x07. */
+ if (!tscm->on_sysex[port]) {
+ consume = i + 1;
+ *label = (port << 4) | 0x07;
+ /* During exclusive message, use label 0x04. */
+ } else if (consume == 3) {
+ *label = (port << 4) | 0x04;
+ /* We need to fill whole 3 bytes. Go to next change. */
+ } else {
+ return 0;
+ }
+
+ len = consume;
+ } else {
+ /* The beginning of exclusives. */
+ if (msg[0] == 0xf0) {
+ /* Transfer it in next chance in another condition. */
+ tscm->on_sysex[port] = true;
+ return 0;
+ } else {
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80)
+ status = tscm->running_status[port];
+ else
+ status = msg[0];
+
+ /* Calculate consume bytes. */
+ len = calculate_message_bytes(status);
+ if (len <= 0)
+ return 0;
+
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80) {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len - 1)
+ return 0;
+ consume = len - 1;
+
+ msg[2] = msg[1];
+ msg[1] = msg[0];
+ msg[0] = tscm->running_status[port];
+ } else {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len)
+ return 0;
+ consume = len;
+
+ tscm->running_status[port] = msg[0];
+ }
+ }
+
+ *label = (port << 4) | (msg[0] >> 4);
+ }
+
+ if (len > 0 && len < 3)
+ memset(msg + len, 0, 3 - len);
+
+ return consume;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_tscm *tscm = callback_data;
+ u32 *buf = (u32 *)data;
+ unsigned int messages;
+ unsigned int i;
+ unsigned int port;
+ struct snd_rawmidi_substream *substream;
+ u8 *b;
+ int bytes;
+
+ if (offset != tscm->async_handler.offset)
+ goto end;
+
+ messages = length / 8;
+ for (i = 0; i < messages; i++) {
+ b = (u8 *)(buf + i * 2);
+
+ port = b[0] >> 4;
+ /* TODO: support virtual MIDI ports. */
+ if (port >= tscm->spec->midi_capture_ports)
+ goto end;
+
+ /* Assume the message length. */
+ bytes = calculate_message_bytes(b[1]);
+ /* On MIDI data or exclusives. */
+ if (bytes <= 0) {
+ /* Seek the end of exclusives. */
+ for (bytes = 1; bytes < 4; bytes++) {
+ if (b[bytes] == 0xf7)
+ break;
+ }
+ if (bytes == 4)
+ bytes = 3;
+ }
+
+ substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]);
+ if (substream != NULL)
+ snd_rawmidi_receive(substream, b + 1, bytes);
+ }
+end:
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ unsigned int i;
+ int err;
+
+ /*
+ * Usually, two quadlets are transferred by one transaction. The first
+ * quadlet has MIDI messages, the rest includes timestamp.
+ * Sometimes, 8 set of the data is transferred by a block transaction.
+ */
+ tscm->async_handler.length = 8 * 8;
+ tscm->async_handler.address_callback = handle_midi_tx;
+ tscm->async_handler.callback_data = tscm;
+
+ err = fw_core_add_address_handler(&tscm->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_tscm_transaction_reregister(tscm);
+ if (err < 0)
+ goto error;
+
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+ err = snd_fw_async_midi_port_init(
+ &tscm->out_ports[i], tscm->unit,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+ 4, fill_message);
+ if (err < 0)
+ goto error;
+ }
+
+ return err;
+error:
+ fw_core_remove_address_handler(&tscm->async_handler);
+ return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+ struct fw_device *device = fw_parent_device(tscm->unit);
+ __be32 reg;
+ int err;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ reg = cpu_to_be32((device->card->node_id << 16) |
+ (tscm->async_handler.offset >> 32));
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(tscm->async_handler.offset);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on messaging. */
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on FireWire LED. */
+ reg = cpu_to_be32(0x0001008e);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ unsigned int i;
+
+ /* Turn off FireWire LED. */
+ reg = cpu_to_be32(0x0000008e);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+
+ /* Turn off messaging. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+
+ /* Unregister the address. */
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&tscm->async_handler);
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
+ snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
+}
diff --git a/kernel/sound/firewire/tascam/tascam.c b/kernel/sound/firewire/tascam/tascam.c
new file mode 100644
index 000000000..ee0bc1839
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam.c
@@ -0,0 +1,209 @@
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static struct snd_tscm_spec model_specs[] = {
+ {
+ .name = "FW-1884",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 8,
+ .midi_capture_ports = 4,
+ .midi_playback_ports = 4,
+ .is_controller = true,
+ },
+ {
+ .name = "FW-1082",
+ .has_adat = false,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 2,
+ .is_controller = true,
+ },
+ /* FW-1804 may be supported. */
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+ struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+ const u32 *config_rom = fw_dev->config_rom;
+ char model[9];
+ unsigned int i;
+ u8 c;
+
+ if (fw_dev->config_rom_length < 30) {
+ dev_err(&tscm->unit->device,
+ "Configuration ROM is too short.\n");
+ return -ENODEV;
+ }
+
+ /* Pick up model name from certain addresses. */
+ for (i = 0; i < 8; i++) {
+ c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+ if (c == '\0')
+ break;
+ model[i] = c;
+ }
+ model[i] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+ if (strcmp(model, model_specs[i].name) == 0) {
+ tscm->spec = &model_specs[i];
+ break;
+ }
+ }
+ if (tscm->spec == NULL)
+ return -ENODEV;
+
+ strcpy(tscm->card->driver, "FW-TASCAM");
+ strcpy(tscm->card->shortname, model);
+ strcpy(tscm->card->mixername, model);
+ snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+ "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+ struct snd_tscm *tscm = card->private_data;
+
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+
+ fw_unit_put(tscm->unit);
+
+ mutex_destroy(&tscm->mutex);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_tscm *tscm;
+ int err;
+
+ /* create card */
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(struct snd_tscm), &card);
+ if (err < 0)
+ return err;
+ card->private_free = tscm_card_free;
+
+ /* initialize myself */
+ tscm = card->private_data;
+ tscm->card = card;
+ tscm->unit = fw_unit_get(unit);
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ err = identify_model(tscm);
+ if (err < 0)
+ goto error;
+
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_stream_init_duplex(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_pcm_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_transaction_register(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_midi_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_hwdep_device(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, tscm);
+
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ snd_tscm_transaction_reregister(tscm);
+
+ mutex_lock(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+ mutex_unlock(&tscm->mutex);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ },
+ /* FE-08 requires reverse-engineering because it just has faders. */
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-firewire-tascam",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_tscm_probe,
+ .update = snd_tscm_update,
+ .remove = snd_tscm_remove,
+ .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+ return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+ driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/kernel/sound/firewire/tascam/tascam.h b/kernel/sound/firewire/tascam/tascam.h
new file mode 100644
index 000000000..2d028d2bd
--- /dev/null
+++ b/kernel/sound/firewire/tascam/tascam.h
@@ -0,0 +1,147 @@
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+ const char *const name;
+ bool has_adat;
+ bool has_spdif;
+ unsigned int pcm_capture_analog_channels;
+ unsigned int pcm_playback_analog_channels;
+ unsigned int midi_capture_ports;
+ unsigned int midi_playback_ports;
+ bool is_controller;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX 4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_tscm {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_tscm_spec *spec;
+
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_counter;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For MIDI message incoming transactions. */
+ struct fw_address_handler async_handler;
+ struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+ /* For MIDI message outgoing transactions. */
+ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+ u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
+ bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
+
+ /* For control messages. */
+ struct snd_firewire_tascam_status *status;
+};
+
+#define TSCM_ADDR_BASE 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM 0x0008
+#define TSCM_OFFSET_FIRMWARE_HW 0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH 0x0200
+#define TSCM_OFFSET_UNKNOWN 0x0204
+#define TSCM_OFFSET_START_STREAMING 0x0208
+#define TSCM_OFFSET_ISOC_RX_CH 0x020c
+#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c
+#define TSCM_OFFSET_ISOC_TX_ON 0x0220
+/* Unknown 0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS 0x0228
+#define TSCM_OFFSET_SET_OPTION 0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON 0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308
+
+#define TSCM_OFFSET_LED_POWER 0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
+
+enum snd_tscm_clock {
+ SND_TSCM_CLOCK_INTERNAL = 0,
+ SND_TSCM_CLOCK_WORD = 1,
+ SND_TSCM_CLOCK_SPDIF = 2,
+ SND_TSCM_CLOCK_ADAT = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+ enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
diff --git a/kernel/sound/hda/Kconfig b/kernel/sound/hda/Kconfig
index 001c6588a..312954639 100644
--- a/kernel/sound/hda/Kconfig
+++ b/kernel/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/kernel/sound/hda/Makefile b/kernel/sound/hda/Makefile
index 7a359f5b7..7e999c995 100644
--- a/kernel/sound/hda/Makefile
+++ b/kernel/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/kernel/sound/hda/ext/Makefile b/kernel/sound/hda/ext/Makefile
new file mode 100644
index 000000000..9b6f641c7
--- /dev/null
+++ b/kernel/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/kernel/sound/hda/ext/hdac_ext_bus.c b/kernel/sound/hda/ext/hdac_ext_bus.c
new file mode 100644
index 000000000..2433f7c81
--- /dev/null
+++ b/kernel/sound/hda/ext/hdac_ext_bus.c
@@ -0,0 +1,265 @@
+/*
+ * 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 <linux/io.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_bus_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_ext_device *edev;
+ struct hdac_device *hdev = NULL;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ char name[15];
+ int ret;
+
+ edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+ hdev = &edev->hdac;
+
+ 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)
+{
+ struct hdac_ext_device *edev = to_ehdac_device(hdev);
+
+ snd_hdac_device_exit(hdev);
+ kfree(edev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit);
+
+/**
+ * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices
+ *
+ * @ebus: HD-audio extended bus
+ */
+void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus)
+{
+ struct hdac_device *codec, *__codec;
+ /*
+ * we need to remove all the codec devices objects created in the
+ * snd_hdac_ext_bus_device_init
+ */
+ list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) {
+ snd_hdac_device_unregister(codec);
+ put_device(&codec->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove);
+#define dev_to_hdac(dev) (container_of((dev), \
+ struct hdac_device, dev))
+
+static inline struct hdac_ext_driver *get_edrv(struct device *dev)
+{
+ struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
+ struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv);
+
+ return edrv;
+}
+
+static inline struct hdac_ext_device *get_edev(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_ext_device *edev = to_ehdac_device(hdev);
+
+ return edev;
+}
+
+static int hda_ext_drv_probe(struct device *dev)
+{
+ return (get_edrv(dev))->probe(get_edev(dev));
+}
+
+static int hdac_ext_drv_remove(struct device *dev)
+{
+ return (get_edrv(dev))->remove(get_edev(dev));
+}
+
+static void hdac_ext_drv_shutdown(struct device *dev)
+{
+ return (get_edrv(dev))->shutdown(get_edev(dev));
+}
+
+/**
+ * snd_hda_ext_driver_register - register a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+int snd_hda_ext_driver_register(struct hdac_ext_driver *drv)
+{
+ drv->hdac.type = HDA_DEV_ASOC;
+ drv->hdac.driver.bus = &snd_hda_bus_type;
+ /* we use default match */
+
+ if (drv->probe)
+ drv->hdac.driver.probe = hda_ext_drv_probe;
+ if (drv->remove)
+ drv->hdac.driver.remove = hdac_ext_drv_remove;
+ if (drv->shutdown)
+ drv->hdac.driver.shutdown = hdac_ext_drv_shutdown;
+
+ return driver_register(&drv->hdac.driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
+
+/**
+ * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv)
+{
+ driver_unregister(&drv->hdac.driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister);
diff --git a/kernel/sound/hda/ext/hdac_ext_controller.c b/kernel/sound/hda/ext/hdac_ext_controller.c
new file mode 100644
index 000000000..63215b172
--- /dev/null
+++ b/kernel/sound/hda/ext/hdac_ext_controller.c
@@ -0,0 +1,302 @@
+/*
+ * 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);
+
+ /* Lets walk the linked capabilities list */
+ do {
+ cur_cap = _snd_hdac_chip_read(l, bus, offset);
+
+ 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 = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
+ hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
+
+ list_add_tail(&hlink->list, &ebus->hlink_list);
+ }
+
+ 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 = readl(link->ml_addr + AZX_REG_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_updatel(link->ml_addr, AZX_REG_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_updatel(link->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
+
+ return check_hdac_link_power_active(link, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
+
+/**
+ * snd_hdac_ext_bus_link_power_down_all -power down all hda link
+ * @ebus: HD-audio extended bus
+ */
+int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
+{
+ struct hdac_ext_link *hlink = NULL;
+ int ret;
+
+ list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
+ ret = check_hdac_link_power_active(hlink, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
diff --git a/kernel/sound/hda/ext/hdac_ext_stream.c b/kernel/sound/hda/ext/hdac_ext_stream.c
new file mode 100644
index 000000000..cb89ec7c8
--- /dev/null
+++ b/kernel/sound/hda/ext/hdac_ext_stream.c
@@ -0,0 +1,499 @@
+/*
+ * 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;
+ }
+
+ if (ebus->spbcap) {
+ stream->spib_addr = ebus->spbcap + AZX_SPB_BASE +
+ AZX_SPB_INTERVAL * idx +
+ AZX_SPB_SPIB;
+
+ stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE +
+ AZX_SPB_INTERVAL * idx +
+ AZX_SPB_MAXFIFO;
+ }
+
+ stream->decoupled = false;
+ snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
+}
+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), 1 << stream);
+}
+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;
+
+ 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;
+
+ 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.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 && !stream->link_locked)
+ snd_hdac_ext_stream_decouple(ebus, stream, false);
+ snd_hdac_stream_release(&stream->hstream);
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ if (stream->decoupled && !stream->hstream.opened)
+ 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 = readl(ebus->spbcap + AZX_REG_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_stream_set_spib - sets the spib value of a stream
+ * @ebus: HD-audio ext core bus
+ * @stream: hdac_ext_stream
+ * @value: spib value to set
+ */
+int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
+ struct hdac_ext_stream *stream, u32 value)
+{
+ struct hdac_bus *bus = &ebus->bus;
+
+ if (!ebus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL");
+ return -EINVAL;
+ }
+
+ writel(value, stream->spib_addr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
+
+/**
+ * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
+ * @ebus: HD-audio ext core bus
+ * @stream: hdac_ext_stream
+ *
+ * Return maxfifo for the stream
+ */
+int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
+ struct hdac_ext_stream *stream)
+{
+ struct hdac_bus *bus = &ebus->bus;
+
+ if (!ebus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL");
+ return -EINVAL;
+ }
+
+ return readl(stream->fifo_addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
+
+
+/**
+ * snd_hdac_ext_stop_streams - stop all stream if running
+ * @ebus: HD-audio ext core bus
+ */
+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/kernel/sound/hda/hda_bus_type.c b/kernel/sound/hda/hda_bus_type.c
index 519914a12..3060e2aee 100644
--- a/kernel/sound/hda/hda_bus_type.c
+++ b/kernel/sound/hda/hda_bus_type.c
@@ -4,12 +4,47 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
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,14 +52,33 @@ 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;
}
+static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ char modalias[32];
+
+ snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+ sizeof(modalias));
+ if (add_uevent_var(env, "MODALIAS=%s", modalias))
+ return -ENOMEM;
+ return 0;
+}
+
struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
+ .uevent = hda_uevent,
};
EXPORT_SYMBOL_GPL(snd_hda_bus_type);
diff --git a/kernel/sound/hda/hdac_bus.c b/kernel/sound/hda/hdac_bus.c
index 8e262da74..0e81ea89a 100644
--- a/kernel/sound/hda/hdac_bus.c
+++ b/kernel/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);
}
@@ -156,6 +172,15 @@ static void process_unsol_events(struct work_struct *work)
}
}
+/**
+ * snd_hdac_bus_add_device - Add a codec to bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to add
+ *
+ * Adds the given codec to the list in the bus. The caddr_tbl array
+ * and codec_powered bits are updated, as well.
+ * Returns zero if success, or a negative error code.
+ */
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
{
if (bus->caddr_tbl[codec->addr]) {
@@ -172,6 +197,11 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
+/**
+ * snd_hdac_bus_remove_device - Remove a codec from bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to remove
+ */
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec)
{
diff --git a/kernel/sound/hda/hdac_controller.c b/kernel/sound/hda/hdac_controller.c
new file mode 100644
index 000000000..b5a17cb51
--- /dev/null
+++ b/kernel/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/kernel/sound/hda/hdac_device.c b/kernel/sound/hda/hdac_device.c
index f75bf5622..e361024ea 100644
--- a/kernel/sound/hda/hdac_device.c
+++ b/kernel/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);
@@ -163,6 +164,43 @@ void snd_hdac_device_unregister(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+ char *newname;
+
+ if (!name)
+ return 0;
+ newname = kstrdup(name, GFP_KERNEL);
+ if (!newname)
+ return -ENOMEM;
+ kfree(codec->chip_name);
+ codec->chip_name = newname;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
+{
+ return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+ codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
+/**
* snd_hdac_make_cmd - compose a 32bit command word to be sent to the
* HD-audio controller
* @codec: the codec object
@@ -371,6 +409,36 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)
}
EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
+/**
+ * snd_hdac_refresh_widget_sysfs - Reset the codec widgets and reinit the
+ * codec sysfs
+ * @codec: the codec object
+ *
+ * first we need to remove sysfs, then refresh widgets and lastly
+ * recreate it
+ */
+int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec)
+{
+ int ret;
+
+ if (device_is_registered(&codec->dev))
+ hda_widget_sysfs_exit(codec);
+ ret = snd_hdac_refresh_widgets(codec);
+ if (ret) {
+ dev_err(&codec->dev, "failed to refresh widget: %d\n", ret);
+ return ret;
+ }
+ if (device_is_registered(&codec->dev)) {
+ ret = hda_widget_sysfs_init(codec);
+ if (ret) {
+ dev_err(&codec->dev, "failed to init sysfs: %d\n", ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_refresh_widget_sysfs);
+
/* return CONNLIST_LEN parameter of the given widget */
static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
{
@@ -500,23 +568,27 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
* This function calls the runtime PM helper to power up the given codec.
* Unlike snd_hdac_power_up_pm(), you should call this only for the code
* path that isn't included in PM path. Otherwise it gets stuck.
+ *
+ * Returns zero if successful, or a negative error code.
*/
-void snd_hdac_power_up(struct hdac_device *codec)
+int snd_hdac_power_up(struct hdac_device *codec)
{
- pm_runtime_get_sync(&codec->dev);
+ return pm_runtime_get_sync(&codec->dev);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_up);
/**
* snd_hdac_power_down - power down the codec
* @codec: the codec object
+ *
+ * Returns zero if successful, or a negative error code.
*/
-void snd_hdac_power_down(struct hdac_device *codec)
+int snd_hdac_power_down(struct hdac_device *codec)
{
struct device *dev = &codec->dev;
pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
+ return pm_runtime_put_autosuspend(dev);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_down);
@@ -528,11 +600,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_power_down);
* which may be called by PM suspend/resume again. OTOH, if a power-up
* call must wake up the sleeper (e.g. in a kctl callback), use
* snd_hdac_power_up() instead.
+ *
+ * Returns zero if successful, or a negative error code.
*/
-void snd_hdac_power_up_pm(struct hdac_device *codec)
+int snd_hdac_power_up_pm(struct hdac_device *codec)
{
if (!atomic_inc_not_zero(&codec->in_pm))
- snd_hdac_power_up(codec);
+ return snd_hdac_power_up(codec);
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
@@ -542,15 +617,35 @@ EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
*
* Like snd_hdac_power_up_pm(), this function is used in a recursive
* code path like init code which may be called by PM suspend/resume again.
+ *
+ * Returns zero if successful, or a negative error code.
*/
-void snd_hdac_power_down_pm(struct hdac_device *codec)
+int snd_hdac_power_down_pm(struct hdac_device *codec)
{
if (atomic_dec_if_positive(&codec->in_pm) < 0)
- snd_hdac_power_down(codec);
+ return snd_hdac_power_down(codec);
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif
+/**
+ * snd_hdac_link_power - Enable/disable the link power for a codec
+ * @codec: the codec object
+ * @bool: enable or disable the link power
+ */
+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 +692,383 @@ 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);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+ unsigned int res;
+
+ if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+ return -1;
+
+ return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+ return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/**
+ * snd_hdac_check_power_state - check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+ hda_nid_t nid, unsigned int target_state)
+{
+ unsigned int state = codec_read(hdac, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+
+ if (state & AC_PWRST_ERROR)
+ return true;
+ state = (state >> 4) & 0x0f;
+ return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
diff --git a/kernel/sound/hda/hdac_i915.c b/kernel/sound/hda/hdac_i915.c
new file mode 100644
index 000000000..8fef1b8d1
--- /dev/null
+++ b/kernel/sound/hda/hdac_i915.c
@@ -0,0 +1,279 @@
+/*
+ * 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;
+
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
+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);
+
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @enable: power up or down
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function manages a refcount and calls the i915 get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ *
+ * Returns zero for success or a negative error code.
+ */
+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);
+ snd_hdac_set_codec_wakeup(bus, true);
+ snd_hdac_set_codec_wakeup(bus, false);
+ }
+ } 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);
+
+/**
+ * snd_hdac_get_display_clk - Get CDCLK in kHz
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function queries CDCLK value in kHz from the graphics driver and
+ * returns the value. A negative code is returned in error.
+ */
+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");
+}
+
+/**
+ * snd_hdac_i915_register_notifier - Register i915 audio component ops
+ * @aops: i915 audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function sets the given ops to be called by the i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
+{
+ if (WARN_ON(!hdac_acomp))
+ return -ENODEV;
+
+ hdac_acomp->audio_ops = aops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
+
+/**
+ * snd_hdac_i915_init - Initialize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+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);
+
+/**
+ * snd_hdac_i915_exit - Finalize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function releases the i915 audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
+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/kernel/sound/hda/hdac_regmap.c b/kernel/sound/hda/hdac_regmap.c
index 1eabcdf69..eb8f7c30c 100644
--- a/kernel/sound/hda/hdac_regmap.c
+++ b/kernel/sound/hda/hdac_regmap.c
@@ -339,6 +339,12 @@ static const struct regmap_config hda_regmap_cfg = {
.use_single_rw = true,
};
+/**
+ * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
+ * @codec: the codec object
+ *
+ * Returns zero for success or a negative error code.
+ */
int snd_hdac_regmap_init(struct hdac_device *codec)
{
struct regmap *regmap;
@@ -352,6 +358,10 @@ int snd_hdac_regmap_init(struct hdac_device *codec)
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
+/**
+ * snd_hdac_regmap_init - Release the regmap from HDA codec
+ * @codec: the codec object
+ */
void snd_hdac_regmap_exit(struct hdac_device *codec)
{
if (codec->regmap) {
@@ -410,8 +420,9 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
err = reg_raw_write(codec, reg, val);
if (err == -EAGAIN) {
- snd_hdac_power_up_pm(codec);
- err = reg_raw_write(codec, reg, val);
+ err = snd_hdac_power_up_pm(codec);
+ if (!err)
+ err = reg_raw_write(codec, reg, val);
snd_hdac_power_down_pm(codec);
}
return err;
@@ -442,8 +453,9 @@ int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
err = reg_raw_read(codec, reg, val);
if (err == -EAGAIN) {
- snd_hdac_power_up_pm(codec);
- err = reg_raw_read(codec, reg, val);
+ err = snd_hdac_power_up_pm(codec);
+ if (!err)
+ err = reg_raw_read(codec, reg, val);
snd_hdac_power_down_pm(codec);
}
return err;
diff --git a/kernel/sound/hda/hdac_stream.c b/kernel/sound/hda/hdac_stream.c
new file mode 100644
index 000000000..38990a77d
--- /dev/null
+++ b/kernel/sound/hda/hdac_stream.c
@@ -0,0 +1,720 @@
+/*
+ * 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);
+
+/**
+ * snd_hdac_get_stream - return hdac_stream based on stream_tag and
+ * direction
+ *
+ * @bus: HD-audio core bus
+ * @dir: direction for the stream to be found
+ * @stream_tag: stream tag for stream to be found
+ */
+struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
+ int dir, int stream_tag)
+{
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (s->direction == dir && s->stream_tag == stream_tag)
+ return s;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream);
+
+/*
+ * set up a BDL entry
+ */
+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/kernel/sound/hda/hdac_sysfs.c b/kernel/sound/hda/hdac_sysfs.c
index 0a6ce3b84..42d61bf41 100644
--- a/kernel/sound/hda/hdac_sysfs.c
+++ b/kernel/sound/hda/hdac_sysfs.c
@@ -45,6 +45,13 @@ CODEC_ATTR(mfg);
CODEC_ATTR_STR(vendor_name);
CODEC_ATTR_STR(chip_name);
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
static struct attribute *hdac_dev_attrs[] = {
&dev_attr_type.attr,
&dev_attr_vendor_id.attr,
@@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = {
&dev_attr_mfg.attr,
&dev_attr_vendor_name.attr,
&dev_attr_chip_name.attr,
+ &dev_attr_modalias.attr,
NULL
};
@@ -321,8 +329,7 @@ static void widget_tree_free(struct hdac_device *codec)
free_widget_node(*p, &widget_node_group);
kfree(tree->nodes);
}
- if (tree->root)
- kobject_put(tree->root);
+ kobject_put(tree->root);
kfree(tree);
codec->widgets = NULL;
}
@@ -391,6 +398,9 @@ int hda_widget_sysfs_init(struct hdac_device *codec)
{
int err;
+ if (codec->widgets)
+ return 0; /* already created */
+
err = widget_tree_create(codec);
if (err < 0) {
widget_tree_free(codec);
diff --git a/kernel/sound/hda/trace.h b/kernel/sound/hda/trace.h
index 33a7eb557..e27e2c0b7 100644
--- a/kernel/sound/hda/trace.h
+++ b/kernel/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/kernel/sound/i2c/other/ak4xxx-adda.c b/kernel/sound/i2c/other/ak4xxx-adda.c
index c65731088..bf377dc19 100644
--- a/kernel/sound/i2c/other/ak4xxx-adda.c
+++ b/kernel/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/kernel/sound/isa/Kconfig b/kernel/sound/isa/Kconfig
index 0216475fc..37adcc6cb 100644
--- a/kernel/sound/isa/Kconfig
+++ b/kernel/sound/isa/Kconfig
@@ -3,6 +3,7 @@
config SND_WSS_LIB
tristate
select SND_PCM
+ select SND_TIMER
config SND_SB_COMMON
tristate
@@ -42,6 +43,7 @@ config SND_AD1816A
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Analog Devices SoundPort
AD1816A or compatible sound chips.
@@ -209,6 +211,7 @@ config SND_GUSCLASSIC
tristate "Gravis UltraSound Classic"
select SND_RAWMIDI
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Gravis UltraSound Classic
soundcards.
@@ -221,6 +224,7 @@ config SND_GUSEXTREME
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Gravis UltraSound Extreme
soundcards.
diff --git a/kernel/sound/isa/gus/gus_mixer.c b/kernel/sound/isa/gus/gus_mixer.c
index 0dd434140..3b5d9a7a6 100644
--- a/kernel/sound/isa/gus/gus_mixer.c
+++ b/kernel/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/kernel/sound/oss/ad1848.c b/kernel/sound/oss/ad1848.c
index ec1ee07df..10c8de1f8 100644
--- a/kernel/sound/oss/ad1848.c
+++ b/kernel/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/kernel/sound/oss/msnd_pinnacle.c b/kernel/sound/oss/msnd_pinnacle.c
index a8ceef8d1..a8bb4a06b 100644
--- a/kernel/sound/oss/msnd_pinnacle.c
+++ b/kernel/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/kernel/sound/oss/sb_audio.c b/kernel/sound/oss/sb_audio.c
index 048439a16..dc91072f4 100644
--- a/kernel/sound/oss/sb_audio.c
+++ b/kernel/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/kernel/sound/pci/Kconfig b/kernel/sound/pci/Kconfig
index edfc1b8d5..8f6594a7d 100644
--- a/kernel/sound/pci/Kconfig
+++ b/kernel/sound/pci/Kconfig
@@ -25,7 +25,7 @@ config SND_ALS300
select SND_PCM
select SND_AC97_CODEC
select SND_OPL3_LIB
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
@@ -50,7 +50,7 @@ config SND_ALI5451
tristate "ALi M5451 PCI Audio Controller"
select SND_MPU401_UART
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for the integrated AC97 sound
device on motherboards using the ALi M5451 Audio Controller
@@ -155,7 +155,8 @@ config SND_AZT3328
select SND_PCM
select SND_RAWMIDI
select SND_AC97_CODEC
- select ZONE_DMA
+ select SND_TIMER
+ depends on ZONE_DMA
help
Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards.
@@ -463,7 +464,8 @@ config SND_EMU10K1
select SND_HWDEP
select SND_RAWMIDI
select SND_AC97_CODEC
- select ZONE_DMA
+ select SND_TIMER
+ depends on ZONE_DMA
help
Say Y to include support for Sound Blaster PCI 512, Live!,
Audigy and E-mu APS (partially supported) soundcards.
@@ -479,7 +481,7 @@ config SND_EMU10K1X
tristate "Emu10k1X (Dell OEM Version)"
select SND_AC97_CODEC
select SND_RAWMIDI
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for the Dell OEM version of the
Sound Blaster Live!.
@@ -513,7 +515,7 @@ config SND_ES1938
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on ESS Solo-1
(ES1938, ES1946, ES1969) chips.
@@ -525,7 +527,7 @@ config SND_ES1968
tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
select SND_MPU401_UART
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on ESS Maestro
1/2/2E chips.
@@ -612,7 +614,7 @@ config SND_ICE1712
select SND_MPU401_UART
select SND_AC97_CODEC
select BITREVERSE
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on the
ICE1712 (Envy24) chip.
@@ -700,7 +702,7 @@ config SND_LX6464ES
config SND_MAESTRO3
tristate "ESS Allegro/Maestro3"
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on ESS Maestro 3
(Allegro) chips.
@@ -806,7 +808,7 @@ config SND_SIS7019
tristate "SiS 7019 Audio Accelerator"
depends on X86_32
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for the SiS 7019 Audio Accelerator.
@@ -818,7 +820,7 @@ config SND_SONICVIBES
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on the S3
SonicVibes chip.
@@ -830,7 +832,7 @@ config SND_TRIDENT
tristate "Trident 4D-Wave DX/NX; SiS 7018"
select SND_MPU401_UART
select SND_AC97_CODEC
- select ZONE_DMA
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on Trident
4D-Wave DX/NX or SiS 7018 chips.
@@ -889,6 +891,7 @@ config SND_YMFPCI
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ select SND_TIMER
help
Say Y here to include support for Yamaha PCI audio chips -
YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754.
diff --git a/kernel/sound/pci/ac97/Makefile b/kernel/sound/pci/ac97/Makefile
index 41fa322f0..526175333 100644
--- a/kernel/sound/pci/ac97/Makefile
+++ b/kernel/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/kernel/sound/pci/ac97/ac97_local.h b/kernel/sound/pci/ac97/ac97_local.h
index c276a5e3f..941a5062c 100644
--- a/kernel/sound/pci/ac97/ac97_local.h
+++ b/kernel/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/kernel/sound/pci/ad1889.c b/kernel/sound/pci/ad1889.c
index 66ddd981d..1fc6d8bc0 100644
--- a/kernel/sound/pci/ad1889.c
+++ b/kernel/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/kernel/sound/pci/ak4531_codec.c b/kernel/sound/pci/ak4531_codec.c
index 3bf0dc533..2fb1fbba3 100644
--- a/kernel/sound/pci/ak4531_codec.c
+++ b/kernel/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/kernel/sound/pci/ali5451/ali5451.c b/kernel/sound/pci/ali5451/ali5451.c
index c8d499575..36470af7e 100644
--- a/kernel/sound/pci/ali5451/ali5451.c
+++ b/kernel/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/kernel/sound/pci/als300.c b/kernel/sound/pci/als300.c
index 57e034f20..add317639 100644
--- a/kernel/sound/pci/als300.c
+++ b/kernel/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/kernel/sound/pci/als4000.c b/kernel/sound/pci/als4000.c
index a3dea4641..ff39a0c72 100644
--- a/kernel/sound/pci/als4000.c
+++ b/kernel/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/kernel/sound/pci/asihpi/hpioctl.c b/kernel/sound/pci/asihpi/hpioctl.c
index 6610bd096..d17937b92 100644
--- a/kernel/sound/pci/asihpi/hpioctl.c
+++ b/kernel/sound/pci/asihpi/hpioctl.c
@@ -32,6 +32,7 @@
#include <linux/pci.h>
#include <linux/stringify.h>
#include <linux/module.h>
+#include <linux/vmalloc.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
diff --git a/kernel/sound/pci/atiixp.c b/kernel/sound/pci/atiixp.c
index 42a20c806..1028fc8bd 100644
--- a/kernel/sound/pci/atiixp.c
+++ b/kernel/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/kernel/sound/pci/atiixp_modem.c b/kernel/sound/pci/atiixp_modem.c
index 0a38e0816..27ed678a4 100644
--- a/kernel/sound/pci/atiixp_modem.c
+++ b/kernel/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/kernel/sound/pci/au88x0/au88x0.c b/kernel/sound/pci/au88x0/au88x0.c
index 996369134..32092184b 100644
--- a/kernel/sound/pci/au88x0/au88x0.c
+++ b/kernel/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/kernel/sound/pci/aw2/aw2-alsa.c b/kernel/sound/pci/aw2/aw2-alsa.c
index 8d2fee7b3..167714303 100644
--- a/kernel/sound/pci/aw2/aw2-alsa.c
+++ b/kernel/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/kernel/sound/pci/azt3328.c b/kernel/sound/pci/azt3328.c
index 33b2a0af1..07a4acc99 100644
--- a/kernel/sound/pci/azt3328.c
+++ b/kernel/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/kernel/sound/pci/ca0106/Makefile b/kernel/sound/pci/ca0106/Makefile
index dcbae7b31..c1455fc55 100644
--- a/kernel/sound/pci/ca0106/Makefile
+++ b/kernel/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/kernel/sound/pci/ca0106/ca0106_main.c b/kernel/sound/pci/ca0106/ca0106_main.c
index dd75b7536..d3cd95633 100644
--- a/kernel/sound/pci/ca0106/ca0106_main.c
+++ b/kernel/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/kernel/sound/pci/ca0106/ca0106_proc.c b/kernel/sound/pci/ca0106/ca0106_proc.c
index 2c5c28adb..9b2b8b381 100644
--- a/kernel/sound/pci/ca0106/ca0106_proc.c
+++ b/kernel/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/kernel/sound/pci/cmipci.c b/kernel/sound/pci/cmipci.c
index 6cf464d90..24cdcba06 100644
--- a/kernel/sound/pci/cmipci.c
+++ b/kernel/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/kernel/sound/pci/cs46xx/cs46xx_lib.c b/kernel/sound/pci/cs46xx/cs46xx_lib.c
index 8d74004b1..2706f271a 100644
--- a/kernel/sound/pci/cs46xx/cs46xx_lib.c
+++ b/kernel/sound/pci/cs46xx/cs46xx_lib.c
@@ -1864,7 +1864,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - IEC958");
- chip->pcm_rear = pcm;
+ chip->pcm_iec958 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
@@ -2528,7 +2528,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs == 1) {
unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
- if (id2 == 0x592b || id2 == 0x592d) {
+ if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */
err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
if (err < 0)
return err;
@@ -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
@@ -3780,6 +3780,11 @@ static int snd_cs46xx_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1;
snd_pcm_suspend_all(chip->pcm);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ snd_pcm_suspend_all(chip->pcm_rear);
+ snd_pcm_suspend_all(chip->pcm_center_lfe);
+ snd_pcm_suspend_all(chip->pcm_iec958);
+#endif
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
diff --git a/kernel/sound/pci/cs46xx/cs46xx_lib.h b/kernel/sound/pci/cs46xx/cs46xx_lib.h
index 86f14620f..bdf411416 100644
--- a/kernel/sound/pci/cs46xx/cs46xx_lib.h
+++ b/kernel/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/kernel/sound/pci/cs46xx/dsp_spos.c b/kernel/sound/pci/cs46xx/dsp_spos.c
index 5c99efb00..d2951ed4b 100644
--- a/kernel/sound/pci/cs46xx/dsp_spos.c
+++ b/kernel/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/kernel/sound/pci/cs46xx/dsp_spos_scb_lib.c b/kernel/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 2c90c0bde..7488e1b7a 100644
--- a/kernel/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/kernel/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/kernel/sound/pci/cs5535audio/cs5535audio.c b/kernel/sound/pci/cs5535audio/cs5535audio.c
index 963b91255..de409cda5 100644
--- a/kernel/sound/pci/cs5535audio/cs5535audio.c
+++ b/kernel/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/kernel/sound/pci/ctxfi/ctamixer.c b/kernel/sound/pci/ctxfi/ctamixer.c
index c7dc38d41..5fcbb065d 100644
--- a/kernel/sound/pci/ctxfi/ctamixer.c
+++ b/kernel/sound/pci/ctxfi/ctamixer.c
@@ -49,7 +49,7 @@ static int amixer_output_slot(const struct rsc *rsc)
return (amixer_index(rsc) << 4) + 0x4;
}
-static struct rsc_ops amixer_basic_rsc_ops = {
+static const struct rsc_ops amixer_basic_rsc_ops = {
.master = amixer_master,
.next_conj = amixer_next_conj,
.index = amixer_index,
@@ -186,7 +186,7 @@ static int amixer_setup(struct amixer *amixer, struct rsc *input,
return 0;
}
-static struct amixer_rsc_ops amixer_ops = {
+static const struct amixer_rsc_ops amixer_ops = {
.set_input = amixer_set_input,
.set_invalid_squash = amixer_set_invalid_squash,
.set_scale = amixer_set_y,
@@ -357,7 +357,7 @@ static int sum_output_slot(const struct rsc *rsc)
return (sum_index(rsc) << 4) + 0xc;
}
-static struct rsc_ops sum_basic_rsc_ops = {
+static const struct rsc_ops sum_basic_rsc_ops = {
.master = sum_master,
.next_conj = sum_next_conj,
.index = sum_index,
diff --git a/kernel/sound/pci/ctxfi/ctamixer.h b/kernel/sound/pci/ctxfi/ctamixer.h
index 72f42f274..2de18aa65 100644
--- a/kernel/sound/pci/ctxfi/ctamixer.h
+++ b/kernel/sound/pci/ctxfi/ctamixer.h
@@ -58,7 +58,7 @@ struct amixer {
unsigned char idx[8];
struct rsc *input; /* pointer to a resource acting as source */
struct sum *sum; /* Put amixer output to this summation node */
- struct amixer_rsc_ops *ops; /* AMixer specific operations */
+ const struct amixer_rsc_ops *ops; /* AMixer specific operations */
};
struct amixer_rsc_ops {
diff --git a/kernel/sound/pci/ctxfi/ctdaio.c b/kernel/sound/pci/ctxfi/ctdaio.c
index 9b87dd28d..7f089cb43 100644
--- a/kernel/sound/pci/ctxfi/ctdaio.c
+++ b/kernel/sound/pci/ctxfi/ctdaio.c
@@ -83,21 +83,21 @@ static int daio_in_next_conj_20k2(struct rsc *rsc)
return rsc->conj += 0x100;
}
-static struct rsc_ops daio_out_rsc_ops = {
+static const struct rsc_ops daio_out_rsc_ops = {
.master = daio_master,
.next_conj = daio_out_next_conj,
.index = daio_index,
.output_slot = NULL,
};
-static struct rsc_ops daio_in_rsc_ops_20k1 = {
+static const struct rsc_ops daio_in_rsc_ops_20k1 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k1,
.index = NULL,
.output_slot = daio_index,
};
-static struct rsc_ops daio_in_rsc_ops_20k2 = {
+static const struct rsc_ops daio_in_rsc_ops_20k2 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k2,
.index = NULL,
@@ -263,7 +263,7 @@ static int dao_clear_right_input(struct dao *dao)
return 0;
}
-static struct dao_rsc_ops dao_ops = {
+static const struct dao_rsc_ops dao_ops = {
.set_spos = dao_spdif_set_spos,
.commit_write = dao_commit_write,
.get_spos = dao_spdif_get_spos,
@@ -318,7 +318,7 @@ static int dai_commit_write(struct dai *dai)
return 0;
}
-static struct dai_rsc_ops dai_ops = {
+static const struct dai_rsc_ops dai_ops = {
.set_srt_srcl = dai_set_srt_srcl,
.set_srt_srcr = dai_set_srt_srcr,
.set_srt_msr = dai_set_srt_msr,
diff --git a/kernel/sound/pci/ctxfi/ctdaio.h b/kernel/sound/pci/ctxfi/ctdaio.h
index 0ebbf350f..a30be73b0 100644
--- a/kernel/sound/pci/ctxfi/ctdaio.h
+++ b/kernel/sound/pci/ctxfi/ctdaio.h
@@ -51,7 +51,7 @@ struct daio {
struct dao {
struct daio daio;
- struct dao_rsc_ops *ops; /* DAO specific operations */
+ const struct dao_rsc_ops *ops; /* DAO specific operations */
struct imapper **imappers;
struct daio_mgr *mgr;
struct hw *hw;
@@ -60,7 +60,7 @@ struct dao {
struct dai {
struct daio daio;
- struct dai_rsc_ops *ops; /* DAI specific operations */
+ const struct dai_rsc_ops *ops; /* DAI specific operations */
struct hw *hw;
void *ctrl_blk;
};
diff --git a/kernel/sound/pci/ctxfi/cthw20k1.c b/kernel/sound/pci/ctxfi/cthw20k1.c
index 1cac55fd1..9667cbfb0 100644
--- a/kernel/sound/pci/ctxfi/cthw20k1.c
+++ b/kernel/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/kernel/sound/pci/ctxfi/cthw20k2.c b/kernel/sound/pci/ctxfi/cthw20k2.c
index 955ad871e..9dc2950e1 100644
--- a/kernel/sound/pci/ctxfi/cthw20k2.c
+++ b/kernel/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/kernel/sound/pci/ctxfi/ctresource.c b/kernel/sound/pci/ctxfi/ctresource.c
index 1a97e406d..c5124c3c0 100644
--- a/kernel/sound/pci/ctxfi/ctresource.c
+++ b/kernel/sound/pci/ctxfi/ctresource.c
@@ -127,7 +127,7 @@ static int rsc_master(struct rsc *rsc)
return rsc->conj = rsc->idx;
}
-static struct rsc_ops rsc_generic_ops = {
+static const struct rsc_ops rsc_generic_ops = {
.index = rsc_index,
.output_slot = audio_ring_slot,
.master = rsc_master,
diff --git a/kernel/sound/pci/ctxfi/ctresource.h b/kernel/sound/pci/ctxfi/ctresource.h
index 9b746c371..736d9f7e9 100644
--- a/kernel/sound/pci/ctxfi/ctresource.h
+++ b/kernel/sound/pci/ctxfi/ctresource.h
@@ -39,7 +39,7 @@ struct rsc {
u32 msr:4; /* The Master Sample Rate a resource working on */
void *ctrl_blk; /* Chip specific control info block for a resource */
struct hw *hw; /* Chip specific object for hardware access means */
- struct rsc_ops *ops; /* Generic resource operations */
+ const struct rsc_ops *ops; /* Generic resource operations */
};
struct rsc_ops {
diff --git a/kernel/sound/pci/ctxfi/ctsrc.c b/kernel/sound/pci/ctxfi/ctsrc.c
index ec1f08464..a5a72df29 100644
--- a/kernel/sound/pci/ctxfi/ctsrc.c
+++ b/kernel/sound/pci/ctxfi/ctsrc.c
@@ -335,7 +335,7 @@ static int src_default_config_arcrw(struct src *src)
return 0;
}
-static struct src_rsc_ops src_rsc_ops = {
+static const struct src_rsc_ops src_rsc_ops = {
.set_state = src_set_state,
.set_bm = src_set_bm,
.set_sf = src_set_sf,
@@ -611,7 +611,7 @@ static int srcimp_index(const struct rsc *rsc)
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
}
-static struct rsc_ops srcimp_basic_rsc_ops = {
+static const struct rsc_ops srcimp_basic_rsc_ops = {
.master = srcimp_master,
.next_conj = srcimp_next_conj,
.index = srcimp_index,
@@ -662,7 +662,7 @@ static int srcimp_unmap(struct srcimp *srcimp)
return 0;
}
-static struct srcimp_rsc_ops srcimp_ops = {
+static const struct srcimp_rsc_ops srcimp_ops = {
.map = srcimp_map,
.unmap = srcimp_unmap
};
diff --git a/kernel/sound/pci/ctxfi/ctsrc.h b/kernel/sound/pci/ctxfi/ctsrc.h
index da7573c5d..92944a012 100644
--- a/kernel/sound/pci/ctxfi/ctsrc.h
+++ b/kernel/sound/pci/ctxfi/ctsrc.h
@@ -48,7 +48,7 @@ struct src_rsc_ops;
struct src {
struct rsc rsc; /* Basic resource info */
struct src *intlv; /* Pointer to next interleaved SRC in a series */
- struct src_rsc_ops *ops; /* SRC specific operations */
+ const struct src_rsc_ops *ops; /* SRC specific operations */
/* Number of contiguous srcs for interleaved usage */
unsigned char multi;
unsigned char mode; /* Working mode of this SRC resource */
@@ -110,7 +110,7 @@ struct srcimp {
struct imapper *imappers;
unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
struct srcimp_mgr *mgr;
- struct srcimp_rsc_ops *ops;
+ const struct srcimp_rsc_ops *ops;
};
struct srcimp_rsc_ops {
diff --git a/kernel/sound/pci/echoaudio/darla20_dsp.c b/kernel/sound/pci/echoaudio/darla20_dsp.c
index febee5bda..320837ba7 100644
--- a/kernel/sound/pci/echoaudio/darla20_dsp.c
+++ b/kernel/sound/pci/echoaudio/darla20_dsp.c
@@ -44,18 +44,18 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_DARLA20_DSP;
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
chip->clock_state = GD_CLOCK_UNDEF;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/darla24_dsp.c b/kernel/sound/pci/echoaudio/darla24_dsp.c
index 7b4f6fd51..8736b5e81 100644
--- a/kernel/sound/pci/echoaudio/darla24_dsp.c
+++ b/kernel/sound/pci/echoaudio/darla24_dsp.c
@@ -44,17 +44,17 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_DARLA24_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_ESYNC;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/echo3g_dsp.c b/kernel/sound/pci/echoaudio/echo3g_dsp.c
index ae11ce11b..6deb80c42 100644
--- a/kernel/sound/pci/echoaudio/echo3g_dsp.c
+++ b/kernel/sound/pci/echoaudio/echo3g_dsp.c
@@ -59,8 +59,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_ECHO3G_DSP;
/* Load the DSP code and the ASIC on the PCI card and get
@@ -78,8 +78,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->px_analog_in = chip->bx_analog_in = 14;
chip->px_digital_in = chip->bx_digital_in = 16;
chip->px_num = chip->bx_num = 24;
- chip->has_phantom_power = TRUE;
- chip->hasnt_input_nominal_level = TRUE;
+ chip->has_phantom_power = true;
+ chip->hasnt_input_nominal_level = true;
} else if (err == E3G_LAYLA3G_BOX_TYPE) {
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF |
@@ -106,10 +106,10 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->non_audio_spdif = FALSE;
- chip->bad_board = FALSE;
- chip->phantom_power = FALSE;
+ chip->professional_spdif = false;
+ chip->non_audio_spdif = false;
+ chip->bad_board = false;
+ chip->phantom_power = false;
return init_line_levels(chip);
}
diff --git a/kernel/sound/pci/echoaudio/echoaudio.c b/kernel/sound/pci/echoaudio/echoaudio.c
index 862db9a0b..1cb85aeb0 100644
--- a/kernel/sound/pci/echoaudio/echoaudio.c
+++ b/kernel/sound/pci/echoaudio/echoaudio.c
@@ -2245,7 +2245,7 @@ static int snd_echo_resume(struct device *dev)
#ifdef ECHOCARD_HAS_MIDI
if (chip->midi_input_enabled)
- enable_midi_input(chip, TRUE);
+ enable_midi_input(chip, true);
if (chip->midi_out)
snd_echo_midi_output_trigger(chip->midi_out, 1);
#endif
diff --git a/kernel/sound/pci/echoaudio/echoaudio.h b/kernel/sound/pci/echoaudio/echoaudio.h
index 32515227a..152ce1585 100644
--- a/kernel/sound/pci/echoaudio/echoaudio.h
+++ b/kernel/sound/pci/echoaudio/echoaudio.h
@@ -153,9 +153,6 @@
#define _ECHOAUDIO_H_
-#define TRUE 1
-#define FALSE 0
-
#include "echoaudio_dsp.h"
@@ -378,8 +375,8 @@ struct echoaudio {
*/
u8 output_clock; /* Layla20 only */
char meters_enabled; /* VU-meters status */
- char asic_loaded; /* Set TRUE when ASIC loaded */
- char bad_board; /* Set TRUE if DSP won't load */
+ char asic_loaded; /* Set true when ASIC loaded */
+ char bad_board; /* Set true if DSP won't load */
char professional_spdif; /* 0 = consumer; 1 = professional */
char non_audio_spdif; /* 3G - only */
char digital_in_automute; /* Gina24, Layla24, Mona - only */
diff --git a/kernel/sound/pci/echoaudio/echoaudio_3g.c b/kernel/sound/pci/echoaudio/echoaudio_3g.c
index 2fa66dc3e..22c786b8a 100644
--- a/kernel/sound/pci/echoaudio/echoaudio_3g.c
+++ b/kernel/sound/pci/echoaudio/echoaudio_3g.c
@@ -41,7 +41,7 @@ static int check_asic_status(struct echoaudio *chip)
return -EIO;
chip->comm_page->ext_box_status = cpu_to_le32(E3G_ASIC_NOT_LOADED);
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
clear_handshake(chip);
send_vector(chip, DSP_VC_TEST_ASIC);
@@ -55,7 +55,7 @@ static int check_asic_status(struct echoaudio *chip)
if (box_status == E3G_ASIC_NOT_LOADED)
return -ENODEV;
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return box_status & E3G_BOX_TYPE_MASK;
}
@@ -243,7 +243,7 @@ static int load_asic(struct echoaudio *chip)
* 48 kHz, internal clock, S/PDIF RCA mode */
if (box_type >= 0) {
err = write_control_reg(chip, E3G_48KHZ,
- E3G_FREQ_REG_DEFAULT, TRUE);
+ E3G_FREQ_REG_DEFAULT, true);
if (err < 0)
return err;
}
@@ -378,16 +378,16 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
dev_err(chip->card->dev,
diff --git a/kernel/sound/pci/echoaudio/echoaudio_dsp.c b/kernel/sound/pci/echoaudio/echoaudio_dsp.c
index 1a9427aab..15aae2fad 100644
--- a/kernel/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/kernel/sound/pci/echoaudio/echoaudio_dsp.c
@@ -103,8 +103,8 @@ static int write_dsp(struct echoaudio *chip, u32 data)
cond_resched();
}
- chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
- dev_dbg(chip->card->dev, "write_dsp: Set bad_board to TRUE\n");
+ chip->bad_board = true; /* Set true until DSP re-loaded */
+ dev_dbg(chip->card->dev, "write_dsp: Set bad_board to true\n");
return -EIO;
}
@@ -126,8 +126,8 @@ static int read_dsp(struct echoaudio *chip, u32 *data)
cond_resched();
}
- chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
- dev_err(chip->card->dev, "read_dsp: Set bad_board to TRUE\n");
+ chip->bad_board = true; /* Set true until DSP re-loaded */
+ dev_err(chip->card->dev, "read_dsp: Set bad_board to true\n");
return -EIO;
}
@@ -166,7 +166,7 @@ static int read_sn(struct echoaudio *chip)
/* This card has no ASIC, just return ok */
static inline int check_asic_status(struct echoaudio *chip)
{
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return 0;
}
@@ -341,11 +341,11 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
dev_warn(chip->card->dev, "DSP is already loaded!\n");
return 0;
}
- chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->bad_board = true; /* Set true until DSP loaded */
chip->dsp_code = NULL; /* Current DSP code not loaded */
- chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */
+ chip->asic_loaded = false; /* Loading the DSP code will reset the ASIC */
- dev_dbg(chip->card->dev, "load_dsp: Set bad_board to TRUE\n");
+ dev_dbg(chip->card->dev, "load_dsp: Set bad_board to true\n");
/* If this board requires a resident loader, install it. */
#ifdef DSP_56361
@@ -471,7 +471,7 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
}
chip->dsp_code = code; /* Show which DSP code loaded */
- chip->bad_board = FALSE; /* DSP OK */
+ chip->bad_board = false; /* DSP OK */
return 0;
}
udelay(100);
@@ -951,10 +951,10 @@ static int rest_in_peace(struct echoaudio *chip)
/* Stops all active pipes (just to be sure) */
stop_transport(chip, chip->active_mask);
- set_meters_on(chip, FALSE);
+ set_meters_on(chip, false);
#ifdef ECHOCARD_HAS_MIDI
- enable_midi_input(chip, FALSE);
+ enable_midi_input(chip, false);
#endif
/* Go to sleep */
@@ -981,9 +981,9 @@ static int init_dsp_comm_page(struct echoaudio *chip)
/* Init all the basic stuff */
chip->card_name = ECHOCARD_NAME;
- chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->bad_board = true; /* Set true until DSP loaded */
chip->dsp_code = NULL; /* Current DSP code not loaded */
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
memset(chip->comm_page, 0, sizeof(struct comm_page));
/* Init the comm page */
diff --git a/kernel/sound/pci/echoaudio/echoaudio_gml.c b/kernel/sound/pci/echoaudio/echoaudio_gml.c
index 23a099425..834b39e97 100644
--- a/kernel/sound/pci/echoaudio/echoaudio_gml.c
+++ b/kernel/sound/pci/echoaudio/echoaudio_gml.c
@@ -48,7 +48,7 @@ static int check_asic_status(struct echoaudio *chip)
if (read_dsp(chip, &asic_status) < 0) {
dev_err(chip->card->dev,
"check_asic_status: failed on read_dsp\n");
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
return -EIO;
}
@@ -192,7 +192,7 @@ static int set_professional_spdif(struct echoaudio *chip, char prof)
}
}
- if ((err = write_control_reg(chip, control_reg, FALSE)))
+ if ((err = write_control_reg(chip, control_reg, false)))
return err;
chip->professional_spdif = prof;
dev_dbg(chip->card->dev, "set_professional_spdif to %s\n",
diff --git a/kernel/sound/pci/echoaudio/gina20.c b/kernel/sound/pci/echoaudio/gina20.c
index 4fa32a2e9..67bd0c9fc 100644
--- a/kernel/sound/pci/echoaudio/gina20.c
+++ b/kernel/sound/pci/echoaudio/gina20.c
@@ -23,7 +23,7 @@
#define ECHOCARD_HAS_INPUT_GAIN
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
/* Pipe indexes */
#define PX_ANALOG_OUT 0 /* 8 */
diff --git a/kernel/sound/pci/echoaudio/gina20_dsp.c b/kernel/sound/pci/echoaudio/gina20_dsp.c
index 5dafe9260..b2377573d 100644
--- a/kernel/sound/pci/echoaudio/gina20_dsp.c
+++ b/kernel/sound/pci/echoaudio/gina20_dsp.c
@@ -48,19 +48,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_GINA20_DSP;
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
chip->clock_state = GD_CLOCK_UNDEF;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
@@ -69,7 +69,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
- chip->professional_spdif = FALSE;
+ chip->professional_spdif = false;
return init_line_levels(chip);
}
diff --git a/kernel/sound/pci/echoaudio/gina24_dsp.c b/kernel/sound/pci/echoaudio/gina24_dsp.c
index 697176693..8eff2b4f5 100644
--- a/kernel/sound/pci/echoaudio/gina24_dsp.c
+++ b/kernel/sound/pci/echoaudio/gina24_dsp.c
@@ -52,7 +52,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
@@ -76,7 +76,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
@@ -86,8 +86,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -152,7 +152,7 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err) {
control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
}
return err;
}
@@ -226,7 +226,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->sample_rate = rate;
dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, clock);
- return write_control_reg(chip, control_reg, FALSE);
+ return write_control_reg(chip, control_reg, false);
}
@@ -274,7 +274,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -285,17 +285,17 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_CDROM:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
dev_err(chip->card->dev,
@@ -333,7 +333,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
diff --git a/kernel/sound/pci/echoaudio/indigo_dsp.c b/kernel/sound/pci/echoaudio/indigo_dsp.c
index 54edd67ed..c97dc83bb 100644
--- a/kernel/sound/pci/echoaudio/indigo_dsp.c
+++ b/kernel/sound/pci/echoaudio/indigo_dsp.c
@@ -49,16 +49,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/indigodj_dsp.c b/kernel/sound/pci/echoaudio/indigodj_dsp.c
index 2cf5cc092..2428b35f4 100644
--- a/kernel/sound/pci/echoaudio/indigodj_dsp.c
+++ b/kernel/sound/pci/echoaudio/indigodj_dsp.c
@@ -49,16 +49,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DJ_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/indigodjx_dsp.c b/kernel/sound/pci/echoaudio/indigodjx_dsp.c
index 5252863f1..5fbd4a3a3 100644
--- a/kernel/sound/pci/echoaudio/indigodjx_dsp.c
+++ b/kernel/sound/pci/echoaudio/indigodjx_dsp.c
@@ -47,17 +47,17 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DJX_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
err = load_firmware(chip);
if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/indigoio_dsp.c b/kernel/sound/pci/echoaudio/indigoio_dsp.c
index 4e8178762..79b68ba70 100644
--- a/kernel/sound/pci/echoaudio/indigoio_dsp.c
+++ b/kernel/sound/pci/echoaudio/indigoio_dsp.c
@@ -49,16 +49,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_IO_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/indigoiox_dsp.c b/kernel/sound/pci/echoaudio/indigoiox_dsp.c
index 6de3f9bc6..1ae394ea7 100644
--- a/kernel/sound/pci/echoaudio/indigoiox_dsp.c
+++ b/kernel/sound/pci/echoaudio/indigoiox_dsp.c
@@ -47,17 +47,17 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_IOX_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
err = load_firmware(chip);
if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/layla20.c b/kernel/sound/pci/echoaudio/layla20.c
index 12e5d2164..fc8468dab 100644
--- a/kernel/sound/pci/echoaudio/layla20.c
+++ b/kernel/sound/pci/echoaudio/layla20.c
@@ -26,7 +26,7 @@
#define ECHOCARD_HAS_SUPER_INTERLEAVE
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
#define ECHOCARD_HAS_MIDI
diff --git a/kernel/sound/pci/echoaudio/layla20_dsp.c b/kernel/sound/pci/echoaudio/layla20_dsp.c
index f2024a356..5e5b6e288 100644
--- a/kernel/sound/pci/echoaudio/layla20_dsp.c
+++ b/kernel/sound/pci/echoaudio/layla20_dsp.c
@@ -51,8 +51,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_LAYLA20_DSP;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
@@ -62,7 +62,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
@@ -71,7 +71,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
- chip->professional_spdif = FALSE;
+ chip->professional_spdif = false;
return init_line_levels(chip);
}
@@ -113,7 +113,7 @@ static int check_asic_status(struct echoaudio *chip)
u32 asic_status;
int goodcnt, i;
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
for (i = goodcnt = 0; i < 5; i++) {
send_vector(chip, DSP_VC_TEST_ASIC);
@@ -127,7 +127,7 @@ static int check_asic_status(struct echoaudio *chip)
if (asic_status == ASIC_ALREADY_LOADED) {
if (++goodcnt == 3) {
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return 0;
}
}
diff --git a/kernel/sound/pci/echoaudio/layla24_dsp.c b/kernel/sound/pci/echoaudio/layla24_dsp.c
index 4f11e81f6..df28e5117 100644
--- a/kernel/sound/pci/echoaudio/layla24_dsp.c
+++ b/kernel/sound/pci/echoaudio/layla24_dsp.c
@@ -51,8 +51,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_LAYLA24_DSP;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
@@ -64,7 +64,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
if ((err = init_line_levels(chip)) < 0)
return err;
@@ -77,8 +77,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -135,7 +135,7 @@ static int load_asic(struct echoaudio *chip)
err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
FW_LAYLA24_2S_ASIC);
if (err < 0)
- return FALSE;
+ return false;
/* Now give the external ASIC a little time to set up */
mdelay(10);
@@ -147,7 +147,7 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err)
err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ,
- TRUE);
+ true);
return err;
}
@@ -241,7 +241,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
dev_dbg(chip->card->dev,
"set_sample_rate: %d clock %d\n", rate, control_reg);
- return write_control_reg(chip, control_reg, FALSE);
+ return write_control_reg(chip, control_reg, false);
}
@@ -287,7 +287,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -334,17 +334,17 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
short asic;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
asic = FW_LAYLA24_2S_ASIC;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
asic = FW_LAYLA24_2A_ASIC;
break;
default:
@@ -383,7 +383,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
diff --git a/kernel/sound/pci/echoaudio/mia.c b/kernel/sound/pci/echoaudio/mia.c
index 2f7562f1a..62b5240f5 100644
--- a/kernel/sound/pci/echoaudio/mia.c
+++ b/kernel/sound/pci/echoaudio/mia.c
@@ -26,7 +26,7 @@
#define ECHOCARD_HAS_VMIXER
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
#define ECHOCARD_HAS_MIDI
#define ECHOCARD_HAS_LINE_OUT_GAIN
diff --git a/kernel/sound/pci/echoaudio/mia_dsp.c b/kernel/sound/pci/echoaudio/mia_dsp.c
index fdad079ad..8f612a09c 100644
--- a/kernel/sound/pci/echoaudio/mia_dsp.c
+++ b/kernel/sound/pci/echoaudio/mia_dsp.c
@@ -52,19 +52,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_MIA_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
if ((subdevice_id & 0x0000f) == MIA_MIDI_REV)
- chip->has_midi = TRUE;
+ chip->has_midi = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF;
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
diff --git a/kernel/sound/pci/echoaudio/mona_dsp.c b/kernel/sound/pci/echoaudio/mona_dsp.c
index 843c7a5e5..dce9e57d0 100644
--- a/kernel/sound/pci/echoaudio/mona_dsp.c
+++ b/kernel/sound/pci/echoaudio/mona_dsp.c
@@ -52,7 +52,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
@@ -69,7 +69,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
if ((err = load_firmware(chip)) < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
return err;
}
@@ -79,8 +79,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -148,7 +148,7 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err) {
control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
}
return err;
@@ -356,7 +356,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -367,16 +367,16 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
dev_err(chip->card->dev,
@@ -415,7 +415,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, FALSE);
+ err = write_control_reg(chip, control_reg, false);
spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
diff --git a/kernel/sound/pci/emu10k1/Makefile b/kernel/sound/pci/emu10k1/Makefile
index fc5591e77..29b44ca27 100644
--- a/kernel/sound/pci/emu10k1/Makefile
+++ b/kernel/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/kernel/sound/pci/emu10k1/emu10k1_main.c b/kernel/sound/pci/emu10k1/emu10k1_main.c
index a4548147c..28e2f8b42 100644
--- a/kernel/sound/pci/emu10k1/emu10k1_main.c
+++ b/kernel/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/kernel/sound/pci/emu10k1/emumixer.c b/kernel/sound/pci/emu10k1/emumixer.c
index 55e571662..076b11700 100644
--- a/kernel/sound/pci/emu10k1/emumixer.c
+++ b/kernel/sound/pci/emu10k1/emumixer.c
@@ -1741,7 +1741,7 @@ static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_audigy_capture_boost =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Capture Boost",
+ .name = "Mic Extra Boost",
.info = snd_audigy_capture_boost_info,
.get = snd_audigy_capture_boost_get,
.put = snd_audigy_capture_boost_put
@@ -1819,8 +1819,6 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
* the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
- "Master Mono Playback Switch",
- "Master Mono Playback Volume",
"Master Playback Switch",
"Master Playback Volume",
"PCM Out Path & Mute",
@@ -1830,10 +1828,16 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Capture Switch",
"Capture Volume",
"Mic Select",
+ "Headphone Playback Switch",
+ "Headphone Playback Volume",
+ "3D Control - Center",
+ "3D Control - Depth",
+ "3D Control - Switch",
"Video Playback Switch",
"Video Playback Volume",
"Mic Playback Switch",
"Mic Playback Volume",
+ "External Amplifier",
NULL
};
static char *audigy_rename_ctls[] = {
@@ -1842,6 +1846,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
/* "Wave Capture Volume", "PCM Capture Volume", */
"Wave Master Playback Volume", "Master Playback Volume",
"AMic Playback Volume", "Mic Playback Volume",
+ "Master Mono Playback Switch", "Phone Output Playback Switch",
+ "Master Mono Playback Volume", "Phone Output Playback Volume",
NULL
};
static char *audigy_rename_ctls_i2c_adc[] = {
@@ -1867,8 +1873,6 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
* the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
- "Master Mono Playback Switch",
- "Master Mono Playback Volume",
"Capture Source",
"Capture Switch",
"Capture Volume",
@@ -1900,7 +1904,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Aux Playback Volume", "Aux Capture Volume",
"Video Playback Switch", "Video Capture Switch",
"Video Playback Volume", "Video Capture Volume",
-
+ "Master Mono Playback Switch", "Phone Output Playback Switch",
+ "Master Mono Playback Volume", "Phone Output Playback Volume",
NULL
};
@@ -1935,6 +1940,9 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
/* set capture source to mic */
snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
+ /* set mono output (TAD) to mic */
+ snd_ac97_update_bits(emu->ac97, AC97_GENERAL_PURPOSE,
+ 0x0200, 0x0200);
if (emu->card_capabilities->adc_1361t)
c = audigy_remove_ctls_1361t_adc;
else
@@ -1996,11 +2004,6 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
- remove_ctl(card, "Headphone Playback Switch");
- remove_ctl(card, "Headphone Playback Volume");
- remove_ctl(card, "3D Control - Center");
- remove_ctl(card, "3D Control - Depth");
- remove_ctl(card, "3D Control - Switch");
}
if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
return -ENOMEM;
diff --git a/kernel/sound/pci/emu10k1/emuproc.c b/kernel/sound/pci/emu10k1/emuproc.c
index 53745f4c2..cf05229b5 100644
--- a/kernel/sound/pci/emu10k1/emuproc.c
+++ b/kernel/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/kernel/sound/pci/es1938.c b/kernel/sound/pci/es1938.c
index e1858d9d2..8963d7688 100644
--- a/kernel/sound/pci/es1938.c
+++ b/kernel/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/kernel/sound/pci/es1968.c b/kernel/sound/pci/es1968.c
index 059f3846d..e0d9363dc 100644
--- a/kernel/sound/pci/es1968.c
+++ b/kernel/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/kernel/sound/pci/hda/Kconfig b/kernel/sound/pci/hda/Kconfig
index a5ed1c181..e94cfd5c6 100644
--- a/kernel/sound/pci/hda/Kconfig
+++ b/kernel/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/kernel/sound/pci/hda/Makefile b/kernel/sound/pci/hda/Makefile
index af78fb33a..6d83c6e03 100644
--- a/kernel/sound/pci/hda/Makefile
+++ b/kernel/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/kernel/sound/pci/hda/hda_auto_parser.c b/kernel/sound/pci/hda/hda_auto_parser.c
index 03b7399bb..7f57a145a 100644
--- a/kernel/sound/pci/hda/hda_auto_parser.c
+++ b/kernel/sound/pci/hda/hda_auto_parser.c
@@ -887,11 +887,32 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
static bool pin_config_match(struct hda_codec *codec,
const struct hda_pintbl *pins)
{
- for (; pins->nid; pins++) {
- u32 def_conf = snd_hda_codec_get_pincfg(codec, pins->nid);
- if (pins->val != def_conf)
+ int i;
+
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ hda_nid_t nid = pin->nid;
+ u32 cfg = pin->cfg;
+ const struct hda_pintbl *t_pins;
+ int found;
+
+ t_pins = pins;
+ found = 0;
+ for (; t_pins->nid; t_pins++) {
+ if (t_pins->nid == nid) {
+ found = 1;
+ if (t_pins->val == cfg)
+ break;
+ else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
+ break;
+ else
+ return false;
+ }
+ }
+ if (!found && (cfg & 0xf0000000) != 0x40000000)
return false;
}
+
return true;
}
diff --git a/kernel/sound/pci/hda/hda_beep.c b/kernel/sound/pci/hda/hda_beep.c
index 3364dc0fd..c397e7da0 100644
--- a/kernel/sound/pci/hda/hda_beep.c
+++ b/kernel/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/kernel/sound/pci/hda/hda_beep.h b/kernel/sound/pci/hda/hda_beep.h
index 46524ff7e..1052ad380 100644
--- a/kernel/sound/pci/hda/hda_beep.h
+++ b/kernel/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/kernel/sound/pci/hda/hda_bind.c b/kernel/sound/pci/hda/hda_bind.c
index 00aa31c5f..6efadbfb3 100644
--- a/kernel/sound/pci/hda/hda_bind.c
+++ b/kernel/sound/pci/hda/hda_bind.c
@@ -15,29 +15,22 @@
#include "hda_local.h"
/*
- * find a matching codec preset
+ * find a matching codec id
*/
static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
struct hda_codec_driver *driver =
container_of(drv, struct hda_codec_driver, core);
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *list;
/* check probe_id instead of vendor_id if set */
u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+ u32 rev_id = codec->core.revision_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) &&
- (!preset->rev || preset->rev == codec->core.revision_id)) {
- codec->preset = preset;
+ for (list = driver->id; list->vendor_id; list++) {
+ if (list->vendor_id == id &&
+ (!list->rev_id || list->rev_id == rev_id)) {
+ codec->preset = list;
return 1;
}
}
@@ -53,26 +46,45 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
codec->patch_ops.unsol_event(codec, ev);
}
-/* reset the codec name from the preset */
-static int codec_refresh_name(struct hda_codec *codec, const char *name)
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
{
- if (name) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup(name, GFP_KERNEL);
+ int err;
+
+ if (!name)
+ return 0;
+ err = snd_hdac_device_set_chip_name(&codec->core, name);
+ if (err < 0)
+ return err;
+
+ /* update the mixer name */
+ if (!*codec->card->mixername ||
+ codec->bus->mixer_assigned >= codec->core.addr) {
+ snprintf(codec->card->mixername,
+ sizeof(codec->card->mixername), "%s %s",
+ codec->core.vendor_name, codec->core.chip_name);
+ codec->bus->mixer_assigned = codec->core.addr;
}
- return codec->core.chip_name ? 0 : -ENOMEM;
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
static int hda_codec_driver_probe(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
struct module *owner = dev->driver->owner;
+ hda_codec_patch_t patch;
int err;
if (WARN_ON(!codec->preset))
return -EINVAL;
- err = codec_refresh_name(codec, codec->preset->name);
+ err = snd_hda_codec_set_name(codec, codec->preset->name);
if (err < 0)
goto error;
err = snd_hdac_regmap_init(&codec->core);
@@ -84,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev)
goto error;
}
- err = codec->preset->patch(codec);
- if (err < 0)
- goto error_module;
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (patch) {
+ err = patch(codec);
+ if (err < 0)
+ goto error_module;
+ }
err = snd_hda_codec_build_pcms(codec);
if (err < 0)
@@ -159,15 +174,40 @@ static inline bool codec_probed(struct hda_codec *codec)
return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
}
+/* try to auto-load codec module */
+static void request_codec_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+ char modalias[32];
+ const char *mod = NULL;
+
+ switch (codec->probe_id) {
+ case HDA_CODEC_ID_GENERIC_HDMI:
+#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
+ mod = "snd-hda-codec-hdmi";
+#endif
+ break;
+ case HDA_CODEC_ID_GENERIC:
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+ mod = "snd-hda-codec-generic";
+#endif
+ break;
+ default:
+ snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+ mod = modalias;
+ break;
+ }
+
+ if (mod)
+ request_module(mod);
+#endif /* MODULE */
+}
+
/* try to auto-load and bind the codec module */
static void codec_bind_module(struct hda_codec *codec)
{
#ifdef MODULE
- request_module("snd-hda-codec-id:%08x", codec->core.vendor_id);
- if (codec_probed(codec))
- return;
- request_module("snd-hda-codec-id:%04x*",
- (codec->core.vendor_id >> 16) & 0xffff);
+ request_codec_module(codec);
if (codec_probed(codec))
return;
#endif
@@ -204,17 +244,13 @@ static int codec_bind_generic(struct hda_codec *codec)
if (is_likely_hdmi_codec(codec)) {
codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
-#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
- request_module("snd-hda-codec-hdmi");
-#endif
+ request_codec_module(codec);
if (codec_probed(codec))
return 0;
}
codec->probe_id = HDA_CODEC_ID_GENERIC;
-#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
- request_module("snd-hda-codec-generic");
-#endif
+ request_codec_module(codec);
if (codec_probed(codec))
return 0;
return -ENODEV;
@@ -259,11 +295,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
}
}
- /* audio codec should override the mixer name */
- if (codec->core.afg || !*codec->card->mixername)
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername), "%s %s",
- codec->core.vendor_name, codec->core.chip_name);
return 0;
error:
diff --git a/kernel/sound/pci/hda/hda_codec.c b/kernel/sound/pci/hda/hda_codec.c
index 36e8f1236..83741887f 100644
--- a/kernel/sound/pci/hda/hda_codec.c
+++ b/kernel/sound/pci/hda/hda_codec.c
@@ -53,76 +53,6 @@
#define codec_has_clkstop(codec) \
((codec)->core.power_caps & AC_PWRST_CLKSTOP)
-/**
- * snd_hda_get_jack_location - Give a location string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack location, e.g. "Rear", "Front", etc.
- */
-const char *snd_hda_get_jack_location(u32 cfg)
-{
- static char *bases[7] = {
- "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
- };
- static unsigned char specials_idx[] = {
- 0x07, 0x08,
- 0x17, 0x18, 0x19,
- 0x37, 0x38
- };
- static char *specials[] = {
- "Rear Panel", "Drive Bar",
- "Riser", "HDMI", "ATAPI",
- "Mobile-In", "Mobile-Out"
- };
- int i;
- cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
- if ((cfg & 0x0f) < 7)
- return bases[cfg & 0x0f];
- for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
- if (cfg == specials_idx[i])
- return specials[i];
- }
- return "UNKNOWN";
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_jack_location);
-
-/**
- * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack connectivity, i.e. external or internal connection.
- */
-const char *snd_hda_get_jack_connectivity(u32 cfg)
-{
- static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
-
- return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_jack_connectivity);
-
-/**
- * snd_hda_get_jack_type - Give a type string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
- */
-const char *snd_hda_get_jack_type(u32 cfg)
-{
- static char *jack_types[16] = {
- "Line Out", "Speaker", "HP Out", "CD",
- "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
- "Line In", "Aux", "Mic", "Telephony",
- "SPDIF In", "Digital In", "Reserved", "Other"
- };
-
- return jack_types[(cfg & AC_DEFCFG_DEVICE)
- >> AC_DEFCFG_DEVICE_SHIFT];
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_jack_type);
-
/*
* Send and receive a verb - passed to exec_verb override for hdac_device
*/
@@ -146,11 +76,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;
}
@@ -161,50 +91,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
}
/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int flags,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
- unsigned int res;
- if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res))
- return -1;
- return res;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
- return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_write);
-
-/**
* snd_hda_sequence_write - sequence writes
* @codec: the HDA codec
* @seq: VERB array to send
@@ -437,7 +323,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 +353,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 +369,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 +745,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 +773,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;
}
@@ -1064,7 +861,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
if (!codec->modelname) {
- err = -ENODEV;
+ err = -ENOMEM;
goto error;
}
}
@@ -1114,7 +911,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec)
hda_nid_t fg;
int err;
- err = snd_hdac_refresh_widgets(&codec->core);
+ err = snd_hdac_refresh_widget_sysfs(&codec->core);
if (err < 0)
return err;
@@ -3223,6 +3020,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 +3028,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);
@@ -3259,8 +3058,7 @@ static int add_std_chmaps(struct hda_codec *codec)
struct snd_pcm_chmap *chmap;
const struct snd_pcm_chmap_elem *elem;
- if (!pcm || !pcm->pcm || pcm->own_chmap ||
- !hinfo->substreams)
+ if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams)
continue;
elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
@@ -3313,311 +3111,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,14 +3322,9 @@ 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);
+ if (err < 0)
return err;
- }
/* attach a new PCM streams */
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
@@ -3849,7 +3337,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 +3404,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 +4010,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 +4028,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/kernel/sound/pci/hda/hda_codec.h b/kernel/sound/pci/hda/hda_codec.h
index 9075ac28d..373fcad84 100644
--- a/kernel/sound/pci/hda/hda_codec.h
+++ b/kernel/sound/pci/hda/hda_codec.h
@@ -22,6 +22,7 @@
#define __SOUND_HDA_CODEC_H
#include <linux/kref.h>
+#include <linux/mod_devicetable.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -40,32 +41,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 +52,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,37 +65,38 @@ 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 */
int primary_dig_out_type; /* primary digital out PCM type */
+ unsigned int mixer_assigned; /* codec addr for mixer name */
};
+/* from hdac_bus to hda_bus */
+#define to_hda_bus(bus) container_of(bus, struct hda_bus, core)
+
/*
* codec preset
*
* Known codecs have the patch to build and set up the controls/PCMs
* better than the generic parser.
*/
-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);
-};
+typedef int (*hda_codec_patch_t)(struct hda_codec *);
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
#define HDA_CODEC_ID_GENERIC 0x00000201
+#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
+ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+ .api_version = HDA_DEV_LEGACY, \
+ .driver_data = (unsigned long)(_patch) }
+#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
+ HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
+
struct hda_codec_driver {
struct hdac_driver core;
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *id;
};
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
@@ -213,7 +187,7 @@ struct hda_codec {
u32 probe_id; /* overridden id for probing */
/* detected preset */
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *preset;
const char *modelname; /* model name for preset */
/* set by patch */
@@ -281,6 +255,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 +275,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 +301,6 @@ struct hda_codec {
/*
* constructors
*/
-int snd_hda_bus_new(struct snd_card *card, 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);
@@ -337,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec);
/*
* low level functions
*/
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
+static inline unsigned int
+snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int flags,
- unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm);
+ unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
+}
+
+static inline int
+snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
+ unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
+}
+
#define snd_hda_param_read(codec, nid, param) \
snd_hdac_read_parm(&(codec)->core, nid, param)
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
@@ -367,8 +349,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 +440,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 +461,9 @@ 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);
+
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
/*
* power management
@@ -498,13 +481,6 @@ int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
}
/*
- * get widget information
- */
-const char *snd_hda_get_jack_connectivity(u32 cfg);
-const char *snd_hda_get_jack_type(u32 cfg);
-const char *snd_hda_get_jack_location(u32 cfg);
-
-/*
* power saving
*/
#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core)
@@ -526,24 +502,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/kernel/sound/pci/hda/hda_controller.c b/kernel/sound/pci/hda/hda_controller.c
index 26ce99059..22dbfa563 100644
--- a/kernel/sound/pci/hda/hda_controller.c
+++ b/kernel/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);
+ snd_hdac_stream_stop(azx_stream(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();
- }
- }
- 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;
-
- 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;
+ spin_unlock_irq(&bus->reg_lock);
- 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;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
- /* 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));
-
- 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,39 +1029,40 @@ 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->mixer_assigned = -1;
+ 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");
bus->needs_damn_long_delay = 1;
}
+ if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
+ bus->core.align_bdle_4k = true;
+
/* AMD chipsets often cause the communication stalls upon certain
* sequence like the pin-detection. It seems that forcing the synced
* access works around the stall. Grrr...
@@ -1854,12 +1075,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 +1089,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 +1112,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 +1134,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 +1174,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/kernel/sound/pci/hda/hda_controller.h b/kernel/sound/pci/hda/hda_controller.h
index 0efdb094d..7b635d68c 100644
--- a/kernel/sound/pci/hda/hda_controller.h
+++ b/kernel/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;
@@ -357,16 +153,16 @@ struct azx {
unsigned int snoop:1;
unsigned int align_buffer_size:1;
unsigned int region_requested:1;
- unsigned int disabled:1; /* disabled by VGA-switcher */
-
- /* for debugging */
- unsigned int last_cmd[AZX_MAX_CODECS];
+ unsigned int disabled:1; /* disabled by vga_switcheroo */
#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/kernel/sound/pci/hda/hda_controller_trace.h b/kernel/sound/pci/hda/hda_controller_trace.h
new file mode 100644
index 000000000..3e18d99bf
--- /dev/null
+++ b/kernel/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/kernel/sound/pci/hda/hda_eld.c b/kernel/sound/pci/hda/hda_eld.c
index 0e6d7534f..563984dd2 100644
--- a/kernel/sound/pci/hda/hda_eld.c
+++ b/kernel/sound/pci/hda/hda_eld.c
@@ -42,7 +42,7 @@ enum cea_edid_versions {
CEA_EDID_VER_RESERVED = 4,
};
-static char *cea_speaker_allocation_names[] = {
+static const char * const cea_speaker_allocation_names[] = {
/* 0 */ "FL/FR",
/* 1 */ "LFE",
/* 2 */ "FC",
@@ -56,7 +56,7 @@ static char *cea_speaker_allocation_names[] = {
/* 10 */ "FCH",
};
-static char *eld_connection_type_names[4] = {
+static const char * const eld_connection_type_names[4] = {
"HDMI",
"DisplayPort",
"2-reserved",
@@ -94,7 +94,7 @@ enum cea_audio_coding_xtypes {
AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
};
-static char *cea_audio_coding_type_names[] = {
+static const char * const cea_audio_coding_type_names[] = {
/* 0 */ "undefined",
/* 1 */ "LPCM",
/* 2 */ "AC-3",
@@ -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)
@@ -482,14 +482,14 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
struct parsed_hdmi_eld *e = &eld->info;
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
int i;
- static char *eld_version_names[32] = {
+ static const char * const eld_version_names[32] = {
"reserved",
"reserved",
"CEA-861D or below",
[3 ... 30] = "reserved",
[31] = "partial"
};
- static char *cea_edid_version_names[8] = {
+ static const char * const cea_edid_version_names[8] = {
"no CEA EDID Timing Extension block present",
"CEA-861",
"CEA-861-A",
@@ -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/kernel/sound/pci/hda/hda_generic.c b/kernel/sound/pci/hda/hda_generic.c
index 5bc7f2e27..5c4fa8eba 100644
--- a/kernel/sound/pci/hda/hda_generic.c
+++ b/kernel/sound/pci/hda/hda_generic.c
@@ -771,9 +771,6 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps;
unsigned int mask, val;
- if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
- return;
-
caps = query_amp_caps(codec, nid, dir);
val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
@@ -784,12 +781,22 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
update_amp(codec, nid, dir, idx, mask, val);
}
+static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int idx_to_check,
+ bool enable)
+{
+ /* check whether the given amp is still used by others */
+ if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
+ return;
+ activate_amp(codec, nid, dir, idx, idx_to_check, enable);
+}
+
static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
int i, bool enable)
{
hda_nid_t nid = path->path[i];
init_amp(codec, nid, HDA_OUTPUT, 0);
- activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+ check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
}
static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
@@ -817,9 +824,16 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
* when aa-mixer is available, we need to enable the path as well
*/
for (n = 0; n < nums; n++) {
- if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid))
- continue;
- activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+ if (n != idx) {
+ if (conn[n] != spec->mixer_merge_nid)
+ continue;
+ /* when aamix is disabled, force to off */
+ if (!add_aamix) {
+ activate_amp(codec, nid, HDA_INPUT, n, n, false);
+ continue;
+ }
+ }
+ check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
}
}
@@ -1580,6 +1594,12 @@ static bool map_singles(struct hda_codec *codec, int outs,
return found;
}
+static inline bool has_aamix_out_paths(struct hda_gen_spec *spec)
+{
+ return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+ spec->aamix_out_paths[2];
+}
+
/* create a new path including aamix if available, and return its index */
static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
{
@@ -2422,25 +2442,51 @@ static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
}
}
+/* re-initialize the output paths; only called from loopback_mixing_put() */
+static void update_output_paths(struct hda_codec *codec, int num_outs,
+ const int *paths)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active,
+ spec->aamix_mode);
+ }
+}
+
static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int val = ucontrol->value.enumerated.item[0];
if (val == spec->aamix_mode)
return 0;
spec->aamix_mode = val;
- update_aamix_paths(codec, val, spec->out_paths[0],
- spec->aamix_out_paths[0],
- spec->autocfg.line_out_type);
- update_aamix_paths(codec, val, spec->hp_paths[0],
- spec->aamix_out_paths[1],
- AUTO_PIN_HP_OUT);
- update_aamix_paths(codec, val, spec->speaker_paths[0],
- spec->aamix_out_paths[2],
- AUTO_PIN_SPEAKER_OUT);
+ if (has_aamix_out_paths(spec)) {
+ update_aamix_paths(codec, val, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ cfg->line_out_type);
+ update_aamix_paths(codec, val, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, val, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+ } else {
+ update_output_paths(codec, cfg->line_outs, spec->out_paths);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ update_output_paths(codec, cfg->hp_outs, spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ update_output_paths(codec, cfg->speaker_outs,
+ spec->speaker_paths);
+ }
return 1;
}
@@ -2458,12 +2504,13 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
if (!spec->mixer_nid)
return 0;
- if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
- spec->aamix_out_paths[2]))
- return 0;
if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
return -ENOMEM;
spec->have_aamix_ctl = 1;
+ /* if no explicit aamix path is present (e.g. for Realtek codecs),
+ * enable aamix as default -- just for compatibility
+ */
+ spec->aamix_mode = !has_aamix_out_paths(spec);
return 0;
}
@@ -3998,9 +4045,9 @@ static void pin_power_callback(struct hda_codec *codec,
struct hda_jack_callback *jack,
bool on)
{
- if (jack && jack->tbl->nid)
+ if (jack && jack->nid)
sync_power_state_change(codec,
- set_pin_power_jack(codec, jack->tbl->nid, on));
+ set_pin_power_jack(codec, jack->nid, on));
}
/* callback only doing power up -- called at first */
@@ -5172,7 +5219,7 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
int err = 0;
mutex_lock(&spec->pcm_mutex);
- if (!spec->indep_hp_enabled)
+ if (spec->indep_hp && !spec->indep_hp_enabled)
err = -EBUSY;
else
spec->active_streams |= 1 << STREAM_INDEP_HP;
@@ -5664,6 +5711,8 @@ static void init_aamix_paths(struct hda_codec *codec)
if (!spec->have_aamix_ctl)
return;
+ if (!has_aamix_out_paths(spec))
+ return;
update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
spec->aamix_out_paths[0],
spec->autocfg.line_out_type);
@@ -5877,13 +5926,14 @@ error:
return err;
}
-static const struct hda_codec_preset snd_hda_preset_generic[] = {
- { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+static const struct hda_device_id snd_hda_id_generic[] = {
+ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
{} /* terminator */
};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
static struct hda_codec_driver generic_driver = {
- .preset = snd_hda_preset_generic,
+ .id = snd_hda_id_generic,
};
module_hda_codec_driver(generic_driver);
diff --git a/kernel/sound/pci/hda/hda_i915.c b/kernel/sound/pci/hda/hda_i915.c
deleted file mode 100644
index 3052a2b09..000000000
--- a/kernel/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/kernel/sound/pci/hda/hda_intel.c b/kernel/sound/pci/hda/hda_intel.c
index 44dfc7b92..2ff692dd2 100644
--- a/kernel/sound/pci/hda/hda_intel.c
+++ b/kernel/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,
@@ -85,6 +90,8 @@ enum {
#define NVIDIA_HDA_ENABLE_COHBIT 0x01
/* Defines for Intel SCH HDA snoop control */
+#define INTEL_HDA_CGCTL 0x48
+#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6)
#define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
@@ -166,7 +173,7 @@ MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
#ifdef CONFIG_PM
static int param_set_xint(const char *val, const struct kernel_param *kp);
-static struct kernel_param_ops param_ops_xint = {
+static const struct kernel_param_ops param_ops_xint = {
.set = param_set_xint,
.get = param_get_int,
};
@@ -307,6 +314,10 @@ enum {
(AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\
AZX_DCAPS_I915_POWERWELL)
+#define AZX_DCAPS_INTEL_BROXTON \
+ (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\
+ AZX_DCAPS_I915_POWERWELL)
+
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
(AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\
@@ -329,10 +340,11 @@ enum {
#define AZX_DCAPS_PRESET_CTHDA \
(AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_NO_64BIT |\
AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF)
/*
- * VGA-switcher support
+ * vga_switcheroo support
*/
#ifdef SUPPORT_VGA_SWITCHEROO
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
@@ -345,6 +357,11 @@ enum {
((pci)->device == 0x0d0c) || \
((pci)->device == 0x160c))
+#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170)
+#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70)
+#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
+#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci))
+
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -496,11 +513,54 @@ static void azx_init_pci(struct azx *chip)
}
}
+/*
+ * In BXT-P A0, HD-Audio DMA requests is later than expected,
+ * and makes an audio stream sensitive to system latencies when
+ * 24/32 bits are playing.
+ * Adjusting threshold of DMA fifo to force the DMA request
+ * sooner to improve latency tolerance at the expense of power.
+ */
+static void bxt_reduce_dma_latency(struct azx *chip)
+{
+ u32 val;
+
+ val = azx_readl(chip, SKL_EM4L);
+ val &= (0x3 << 20);
+ azx_writel(chip, SKL_EM4L, val);
+}
+
+static void hda_intel_init_chip(struct azx *chip, bool full_reset)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct pci_dev *pci = chip->pci;
+ u32 val;
+
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ snd_hdac_set_codec_wakeup(bus, true);
+ if (IS_SKL_PLUS(pci)) {
+ pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val);
+ val = val & ~INTEL_HDA_CGCTL_MISCBDCGE;
+ pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
+ }
+ azx_init_chip(chip, full_reset);
+ if (IS_SKL_PLUS(pci)) {
+ pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val);
+ val = val | INTEL_HDA_CGCTL_MISCBDCGE;
+ pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
+ }
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ snd_hdac_set_codec_wakeup(bus, false);
+
+ /* reduce dma latency to avoid noise */
+ if (IS_BXT(pci))
+ bxt_reduce_dma_latency(chip);
+}
+
/* 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 +570,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 +608,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 +627,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 +644,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 +656,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 +677,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 +690,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 +718,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 +743,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 +756,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 +766,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 +784,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 +839,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 +851,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,28 +904,33 @@ 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;
chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip);
- if (chip->disabled || hda->init_failed)
+ if (chip->disabled || hda->init_failed || !chip->running)
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;
}
@@ -818,11 +946,12 @@ static int azx_resume(struct device *dev)
chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip);
- if (chip->disabled || hda->init_failed)
+ if (chip->disabled || hda->init_failed || !chip->running)
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,13 +961,40 @@ 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 */
+#ifdef CONFIG_PM_SLEEP
+/* put codec down to D3 at hibernation for Intel SKL+;
+ * otherwise BIOS may still access the codec and screw up the driver
+ */
+static int azx_freeze_noirq(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ if (IS_SKL_PLUS(pci))
+ pci_set_power_state(pci, PCI_D3hot);
+
+ return 0;
+}
+
+static int azx_thaw_noirq(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ if (IS_SKL_PLUS(pci))
+ pci_set_power_state(pci, PCI_D0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
#ifdef CONFIG_PM
static int azx_runtime_suspend(struct device *dev)
{
@@ -864,9 +1020,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 +1033,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;
@@ -891,19 +1049,25 @@ static int azx_runtime_resume(struct device *dev)
return 0;
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- hda_display_power(hda, true);
- haswell_set_bclk(hda);
+ bus = azx_bus(chip);
+ if (hda->need_i915_power) {
+ snd_hdac_display_power(bus, true);
+ haswell_set_bclk(hda);
+ } else {
+ /* 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 +1077,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 +1096,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 || !chip->running)
return -EBUSY;
return 0;
@@ -939,6 +1104,10 @@ static int azx_runtime_idle(struct device *dev)
static const struct dev_pm_ops azx_pm = {
SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+#ifdef CONFIG_PM_SLEEP
+ .freeze_noirq = azx_freeze_noirq,
+ .thaw_noirq = azx_thaw_noirq,
+#endif
SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
};
@@ -969,7 +1138,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,
@@ -980,21 +1149,21 @@ static void azx_vs_set_state(struct pci_dev *pci,
}
}
} else {
- dev_info(chip->card->dev, "%s via VGA-switcheroo\n",
+ dev_info(chip->card->dev, "%s via vga_switcheroo\n",
disabled ? "Disabling" : "Enabling");
if (disabled) {
pm_runtime_put_sync_suspend(card->dev);
azx_suspend(card->dev);
- /* when we get suspended by vga switcheroo we end up in D3cold,
+ /* when we get suspended by vga_switcheroo we end up in D3cold,
* however we have no ACPI handle, so pci/acpi can't put us there,
* 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 +1180,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;
}
@@ -1025,7 +1194,7 @@ static void init_vga_switcheroo(struct azx *chip)
struct pci_dev *p = get_bound_vga(chip->pci);
if (p) {
dev_info(chip->card->dev,
- "Handle VGA-switcheroo audio client\n");
+ "Handle vga_switcheroo audio client\n");
hda->use_vga_switcheroo = 1;
pci_dev_put(p);
}
@@ -1047,8 +1216,7 @@ static int register_vga_switcheroo(struct azx *chip)
* is there any machine with two switchable HDMI audio controllers?
*/
err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops,
- VGA_SWITCHEROO_DIS,
- chip->bus != NULL);
+ VGA_SWITCHEROO_DIS);
if (err < 0)
return err;
hda->vga_switcheroo_registered = 1;
@@ -1071,7 +1239,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 +1250,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);
@@ -1125,7 +1305,7 @@ static int azx_dev_free(struct snd_device *device)
#ifdef SUPPORT_VGA_SWITCHEROO
/*
- * Check of disabled HDMI controller by vga-switcheroo
+ * Check of disabled HDMI controller by vga_switcheroo
*/
static struct pci_dev *get_bound_vga(struct pci_dev *pci)
{
@@ -1284,9 +1464,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 +1553,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 +1581,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 +1616,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 +1643,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 +1663,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 +1683,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 +1725,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 +1760,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 +1780,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 +1793,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 +1862,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 +1877,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 +1894,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 +1910,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 +1936,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 +1980,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;
@@ -1799,7 +1990,7 @@ static int azx_probe(struct pci_dev *pci,
err = register_vga_switcheroo(chip);
if (err < 0) {
- dev_err(card->dev, "Error registering VGA-switcheroo client\n");
+ dev_err(card->dev, "Error registering vga_switcheroo client\n");
goto out_free;
}
@@ -1851,14 +2042,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 +2071,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 +2090,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 +2118,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);
@@ -1937,9 +2137,17 @@ out_free:
static void azx_remove(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip;
+ struct hda_intel *hda;
+
+ if (card) {
+ /* cancel the pending probing work */
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ cancel_work_sync(&hda->probe_work);
- if (card)
snd_card_free(card);
+ }
}
static void azx_shutdown(struct pci_dev *pci)
@@ -1976,6 +2184,11 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
{ PCI_DEVICE(0x8086, 0x8d21),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Lewisburg */
+ { PCI_DEVICE(0x8086, 0xa1f0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE(0x8086, 0xa270),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Lynx Point-LP */
{ PCI_DEVICE(0x8086, 0x9c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
@@ -1991,6 +2204,9 @@ static const struct pci_device_id azx_ids[] = {
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Broxton-P(Apollolake) */
+ { PCI_DEVICE(0x8086, 0x5a98),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
@@ -2156,11 +2372,13 @@ static const struct pci_device_id azx_ids[] = {
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_NO_64BIT |
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
{ PCI_DEVICE(0x1102, 0x0009),
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_NO_64BIT |
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
#endif
/* CM8888 */
diff --git a/kernel/sound/pci/hda/hda_intel.h b/kernel/sound/pci/hda/hda_intel.h
index d5231f721..ff0c4d617 100644
--- a/kernel/sound/pci/hda/hda_intel.h
+++ b/kernel/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,8 +33,9 @@ struct hda_intel {
/* extra flags */
unsigned int irq_pending_warned:1;
+ unsigned int probe_continued:1;
- /* VGA-switcheroo setup */
+ /* vga_switcheroo setup */
unsigned int use_vga_switcheroo:1;
unsigned int vga_switcheroo_registered:1;
unsigned int init_failed:1; /* delayed init failed */
@@ -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/kernel/sound/pci/hda/hda_intel_trace.h b/kernel/sound/pci/hda/hda_intel_trace.h
index 7b5e4c2cf..0922d8b1b 100644
--- a/kernel/sound/pci/hda/hda_intel_trace.h
+++ b/kernel/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/kernel/sound/pci/hda/hda_jack.c b/kernel/sound/pci/hda/hda_jack.c
index d7cfe7b8c..a33234e04 100644
--- a/kernel/sound/pci/hda/hda_jack.c
+++ b/kernel/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);
@@ -259,7 +259,7 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
if (!callback)
return ERR_PTR(-ENOMEM);
callback->func = func;
- callback->tbl = jack;
+ callback->nid = jack->nid;
callback->next = jack->callback;
jack->callback = callback;
}
@@ -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,100 +372,53 @@ 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)
+int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ 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
- return 0;
-}
+ snd_jack_report(jack->jack, state ? jack->type : 0);
-/**
- * snd_hda_jack_add_kctl - Add a jack kctl for the given pin
- * @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)
-{
- return __snd_hda_jack_add_kctl(codec, nid, name, idx, false);
+ return 0;
}
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 +430,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/kernel/sound/pci/hda/hda_jack.h b/kernel/sound/pci/hda/hda_jack.h
index b279e327a..e9814c016 100644
--- a/kernel/sound/pci/hda/hda_jack.h
+++ b/kernel/sound/pci/hda/hda_jack.h
@@ -21,7 +21,7 @@ struct hda_jack_callback;
typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
struct hda_jack_callback {
- struct hda_jack_tbl *tbl;
+ hda_nid_t nid;
hda_jack_callback_fn func;
unsigned int private_data; /* arbitrary data */
struct hda_jack_callback *next;
@@ -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, bool phantom_jack);
int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg);
diff --git a/kernel/sound/pci/hda/hda_local.h b/kernel/sound/pci/hda/hda_local.h
index bed66c314..d0e066e4c 100644
--- a/kernel/sound/pci/hda/hda_local.h
+++ b/kernel/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; }
@@ -681,12 +681,7 @@ static inline bool
snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int target_state)
{
- unsigned int state = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state & AC_PWRST_ERROR)
- return true;
- state = (state >> 4) & 0x0f;
- return (state == target_state);
+ return snd_hdac_check_power_state(&codec->core, nid, target_state);
}
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
@@ -777,7 +772,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/kernel/sound/pci/hda/hda_proc.c b/kernel/sound/pci/hda/hda_proc.c
index baaf7ed06..033aa8436 100644
--- a/kernel/sound/pci/hda/hda_proc.c
+++ b/kernel/sound/pci/hda/hda_proc.c
@@ -36,24 +36,9 @@ MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1
#define param_read(codec, nid, parm) \
snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
-static char *bits_names(unsigned int bits, char *names[], int size)
-{
- int i, n;
- static char buf[128];
-
- for (i = 0, n = 0; i < size; i++) {
- if (bits & (1U<<i) && names[i])
- n += snprintf(buf + n, sizeof(buf) - n, " %s",
- names[i]);
- }
- buf[n] = '\0';
-
- return buf;
-}
-
static const char *get_wid_type_name(unsigned int wid_value)
{
- static char *names[16] = {
+ static const char * const names[16] = {
[AC_WID_AUD_OUT] = "Audio Output",
[AC_WID_AUD_IN] = "Audio Input",
[AC_WID_AUD_MIX] = "Audio Mixer",
@@ -241,7 +226,7 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
static const char *get_jack_connection(u32 cfg)
{
- static char *names[16] = {
+ static const char * const names[16] = {
"Unknown", "1/8", "1/4", "ATAPI",
"RCA", "Optical","Digital", "Analog",
"DIN", "XLR", "RJ11", "Comb",
@@ -256,7 +241,7 @@ static const char *get_jack_connection(u32 cfg)
static const char *get_jack_color(u32 cfg)
{
- static char *names[16] = {
+ static const char * const names[16] = {
"Unknown", "Black", "Grey", "Blue",
"Green", "Red", "Orange", "Yellow",
"Purple", "Pink", NULL, NULL,
@@ -269,11 +254,74 @@ static const char *get_jack_color(u32 cfg)
return "UNKNOWN";
}
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
+static const char *get_jack_location(u32 cfg)
+{
+ static const char * const bases[7] = {
+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+ };
+ static const unsigned char specials_idx[] = {
+ 0x07, 0x08,
+ 0x17, 0x18, 0x19,
+ 0x37, 0x38
+ };
+ static const char * const specials[] = {
+ "Rear Panel", "Drive Bar",
+ "Riser", "HDMI", "ATAPI",
+ "Mobile-In", "Mobile-Out"
+ };
+ int i;
+
+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+ if ((cfg & 0x0f) < 7)
+ return bases[cfg & 0x0f];
+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+ if (cfg == specials_idx[i])
+ return specials[i];
+ }
+ return "UNKNOWN";
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
+static const char *get_jack_connectivity(u32 cfg)
+{
+ static const char * const jack_locations[4] = {
+ "Ext", "Int", "Sep", "Oth"
+ };
+
+ return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
+static const char *get_jack_type(u32 cfg)
+{
+ static const char * const jack_types[16] = {
+ "Line Out", "Speaker", "HP Out", "CD",
+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony",
+ "SPDIF In", "Digital In", "Reserved", "Other"
+ };
+
+ return jack_types[(cfg & AC_DEFCFG_DEVICE)
+ >> AC_DEFCFG_DEVICE_SHIFT];
+}
+
static void print_pin_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int *supports_vref)
{
- static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
+ static const char * const jack_conns[4] = {
+ "Jack", "N/A", "Fixed", "Both"
+ };
unsigned int caps, val;
caps = param_read(codec, nid, AC_PAR_PIN_CAP);
@@ -340,9 +388,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
- snd_hda_get_jack_type(caps),
- snd_hda_get_jack_connectivity(caps),
- snd_hda_get_jack_location(caps));
+ get_jack_type(caps),
+ get_jack_connectivity(caps),
+ get_jack_location(caps));
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
@@ -478,7 +526,7 @@ static const char *get_pwr_state(u32 state)
static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- static char *names[] = {
+ static const char * const names[] = {
[ilog2(AC_PWRST_D0SUP)] = "D0",
[ilog2(AC_PWRST_D1SUP)] = "D1",
[ilog2(AC_PWRST_D2SUP)] = "D2",
@@ -492,9 +540,16 @@ static void print_power_state(struct snd_info_buffer *buffer,
int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
- if (sup != -1)
- snd_iprintf(buffer, " Power states: %s\n",
- bits_names(sup, names, ARRAY_SIZE(names)));
+ if (sup != -1) {
+ int i;
+
+ snd_iprintf(buffer, " Power states: ");
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ if (sup & (1U << i))
+ snd_iprintf(buffer, " %s", names[i]);
+ }
+ snd_iprintf(buffer, "\n");
+ }
snd_iprintf(buffer, " Power: setting=%s, actual=%s",
get_pwr_state(pwr & AC_PWRST_SETTING),
diff --git a/kernel/sound/pci/hda/hda_sysfs.c b/kernel/sound/pci/hda/hda_sysfs.c
index a6e3d9b51..64e0d1d81 100644
--- a/kernel/sound/pci/hda/hda_sysfs.c
+++ b/kernel/sound/pci/hda/hda_sysfs.c
@@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus,
static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
struct hda_codec **codecp)
{
- kfree((*codecp)->core.chip_name);
- (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL);
+ snd_hda_codec_set_name(*codecp, buf);
}
#define DEFINE_PARSE_ID_MODE(name) \
diff --git a/kernel/sound/pci/hda/hda_tegra.c b/kernel/sound/pci/hda/hda_tegra.c
index 2e4fd5c56..58c0aad37 100644
--- a/kernel/sound/pci/hda/hda_tegra.c
+++ b/kernel/sound/pci/hda/hda_tegra.c
@@ -73,6 +73,7 @@ struct hda_tegra {
struct clk *hda2codec_2x_clk;
struct clk *hda2hdmi_clk;
void __iomem *regs;
+ struct work_struct probe_work;
};
#ifdef CONFIG_PM
@@ -87,13 +88,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 +103,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 +169,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 +178,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 +281,31 @@ 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;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
- if (chip->initialized) {
- for (i = 0; i < chip->num_streams; i++)
- azx_stream_stop(chip, &chip->azx_dev[i]);
+ cancel_work_sync(&hda->probe_work);
+ 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 +313,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 +355,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 +373,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 +393,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 +421,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;
}
@@ -407,12 +429,15 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
/*
* constructor
*/
+
+static void hda_tegra_probe_work(struct work_struct *work);
+
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 +445,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 +458,12 @@ static int hda_tegra_create(struct snd_card *card,
chip->single_cmd = false;
chip->snoop = true;
+ INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
+
+ 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 +481,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,22 +501,33 @@ 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;
dev_set_drvdata(&pdev->dev, card);
+ schedule_work(&hda->probe_work);
+
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void hda_tegra_probe_work(struct work_struct *work)
+{
+ struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work);
+ struct azx *chip = &hda->chip;
+ struct platform_device *pdev = to_platform_device(hda->dev);
+ int err;
err = hda_tegra_first_init(chip, pdev);
if (err < 0)
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,13 +541,10 @@ 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);
-
- return 0;
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
-out_free:
- snd_card_free(card);
- return err;
+ out_free:
+ return; /* no error return from async probe */
}
static int hda_tegra_remove(struct platform_device *pdev)
diff --git a/kernel/sound/pci/hda/patch_analog.c b/kernel/sound/pci/hda/patch_analog.c
index 231f89029..e0fb8c6d1 100644
--- a/kernel/sound/pci/hda/patch_analog.c
+++ b/kernel/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;
}
@@ -1166,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
- { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
- { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
- { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
- { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
- { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
- { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
- { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
- { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
- { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
- { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
- { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
- { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
+static const struct hda_device_id snd_hda_id_analog[] = {
+ HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
+ HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
+ HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
+ HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
static struct hda_codec_driver analog_driver = {
- .preset = snd_hda_preset_analog,
+ .id = snd_hda_id_analog,
};
module_hda_codec_driver(analog_driver);
diff --git a/kernel/sound/pci/hda/patch_ca0110.c b/kernel/sound/pci/hda/patch_ca0110.c
index 447302695..c2d9ee9cf 100644
--- a/kernel/sound/pci/hda/patch_ca0110.c
+++ b/kernel/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:
@@ -84,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
- { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+ HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_driver ca0110_driver = {
- .preset = snd_hda_preset_ca0110,
+ .id = snd_hda_id_ca0110,
};
module_hda_codec_driver(ca0110_driver);
diff --git a/kernel/sound/pci/hda/patch_ca0132.c b/kernel/sound/pci/hda/patch_ca0132.c
index 4a4e7b282..9ceb2bc36 100644
--- a/kernel/sound/pci/hda/patch_ca0132.c
+++ b/kernel/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,34 @@ struct ca0132_spec {
};
/*
+ * CA0132 quirks table
+ */
+enum {
+ QUIRK_NONE,
+ QUIRK_ALIENWARE,
+};
+
+static const struct hda_pintbl alienware_pincfgs[] = {
+ { 0x0b, 0x90170110 }, /* Builtin Speaker */
+ { 0x0c, 0x411111f0 }, /* N/A */
+ { 0x0d, 0x411111f0 }, /* N/A */
+ { 0x0e, 0x411111f0 }, /* N/A */
+ { 0x0f, 0x0321101f }, /* HP */
+ { 0x10, 0x411111f0 }, /* Headset? disabled for now */
+ { 0x11, 0x03a11021 }, /* Mic */
+ { 0x12, 0xd5a30140 }, /* Builtin Mic */
+ { 0x13, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ {}
+};
+
+static const struct snd_pci_quirk ca0132_quirks[] = {
+ SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
+ {}
+};
+
+/*
* CA0132 codec access
*/
static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
@@ -2052,11 +2081,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;
@@ -2648,13 +2674,13 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
do {
if (dspload_is_loaded(codec)) {
- pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
+ codec_info(codec, "ca0132 DSP downloaded and running\n");
return true;
}
msleep(20);
} while (time_before(jiffies, timeout));
- pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
+ codec_err(codec, "ca0132 failed to download DSP\n");
return false;
}
@@ -3136,7 +3162,7 @@ static int ca0132_select_out(struct hda_codec *codec)
auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
if (auto_jack)
- jack_present = snd_hda_jack_detect(codec, spec->out_pins[1]);
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp);
else
jack_present =
spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
@@ -3227,7 +3253,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);
@@ -3298,7 +3324,7 @@ static int ca0132_select_mic(struct hda_codec *codec)
auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
if (auto_jack)
- jack_present = snd_hda_jack_detect(codec, spec->input_pins[0]);
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_amic1);
else
jack_present =
spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
@@ -4350,7 +4376,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
- pr_err("ca0132 dspload_image failed.\n");
+ codec_err(codec, "ca0132 DSP load image failed\n");
goto exit_download;
}
@@ -4401,13 +4427,16 @@ static void ca0132_process_dsp_response(struct hda_codec *codec,
static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
{
struct ca0132_spec *spec = codec->spec;
+ struct hda_jack_tbl *tbl;
/* Delay enabling the HP amp, to let the mic-detection
* state machine run.
*/
cancel_delayed_work_sync(&spec->unsol_hp_work);
schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
- cb->tbl->block_report = 1;
+ tbl = snd_hda_jack_tbl_get(codec, cb->nid);
+ if (tbl)
+ tbl->block_report = 1;
}
static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
@@ -4417,8 +4446,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 +4509,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 +4588,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 +4610,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);
}
@@ -4615,36 +4635,106 @@ static void ca0132_config(struct hda_codec *codec)
spec->multiout.num_dacs = 3;
spec->multiout.max_channels = 2;
- spec->num_outputs = 2;
- spec->out_pins[0] = 0x0b; /* speaker out */
- spec->out_pins[1] = 0x10; /* headphone out */
- spec->shared_out_nid = 0x2;
+ if (spec->quirk == QUIRK_ALIENWARE) {
+ codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n");
+ snd_hda_apply_pincfgs(codec, alienware_pincfgs);
+
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x0f;
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = 0x0f;
+
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 3;
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = 0x11;
+ } else {
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x10; /* headphone out */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 3;
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ spec->dig_in = 0x09;
+ cfg->dig_in_pin = 0x0e;
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ }
+}
- spec->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;
+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;
- spec->input_pins[0] = 0x12;
- spec->input_pins[1] = 0x11;
- spec->input_pins[2] = 0x13;
+ /* 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;
- /* SPDIF I/O */
- spec->dig_out = 0x05;
- spec->multiout.dig_out_nid = spec->dig_out;
- cfg->dig_out_pins[0] = 0x0c;
- cfg->dig_outs = 1;
- cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
- spec->dig_in = 0x09;
- cfg->dig_in_pin = 0x0e;
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ /* 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 +4744,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 +4768,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;
}
@@ -4684,18 +4782,17 @@ static int patch_ca0132(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_ca0132[] = {
- { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
+static struct hda_device_id snd_hda_id_ca0132[] = {
+ HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11020011");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative Sound Core3D codec");
static struct hda_codec_driver ca0132_driver = {
- .preset = snd_hda_preset_ca0132,
+ .id = snd_hda_id_ca0132,
};
module_hda_codec_driver(ca0132_driver);
diff --git a/kernel/sound/pci/hda/patch_cirrus.c b/kernel/sound/pci/hda/patch_cirrus.c
index 3a24f7739..c1c855a6c 100644
--- a/kernel/sound/pci/hda/patch_cirrus.c
+++ b/kernel/sound/pci/hda/patch_cirrus.c
@@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
return NULL;
codec->spec = spec;
spec->vendor_nid = vendor_nid;
+ codec->power_save_node = 1;
snd_hda_gen_spec_init(&spec->gen);
return spec;
@@ -584,6 +585,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 +597,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;
@@ -614,6 +614,7 @@ enum {
CS4208_MAC_AUTO,
CS4208_MBA6,
CS4208_MBP11,
+ CS4208_MACMINI,
CS4208_GPIO0,
};
@@ -621,6 +622,7 @@ static const struct hda_model_fixup cs4208_models[] = {
{ .id = CS4208_GPIO0, .name = "gpio0" },
{ .id = CS4208_MBA6, .name = "mba6" },
{ .id = CS4208_MBP11, .name = "mbp11" },
+ { .id = CS4208_MACMINI, .name = "macmini" },
{}
};
@@ -632,8 +634,10 @@ static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
/* codec SSID matching */
static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
+ SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI),
SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
{} /* terminator */
};
@@ -665,6 +669,24 @@ static void cs4208_fixup_mac(struct hda_codec *codec,
snd_hda_apply_fixup(codec, action);
}
+/* MacMini 7,1 has the inverted jack detection */
+static void cs4208_fixup_macmini(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */
+ { 0x21, 0x004be140 }, /* SPDIF: disable detect */
+ { }
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* HP pin (0x10) has an inverted detection */
+ codec->inv_jack_detect = 1;
+ /* disable the bogus Mic and SPDIF jack detections */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -708,6 +730,12 @@ static const struct hda_fixup cs4208_fixups[] = {
.chained = true,
.chain_id = CS4208_GPIO0,
},
+ [CS4208_MACMINI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_macmini,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
[CS4208_GPIO0] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs4208_fixup_gpio0,
@@ -738,6 +766,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 +785,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;
@@ -1148,6 +1175,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,
@@ -1165,8 +1193,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;
@@ -1185,11 +1211,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:
@@ -1201,26 +1228,21 @@ static int patch_cs4213(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
- { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
- { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
- { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
- { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
- { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
+static const struct hda_device_id snd_hda_id_cirrus[] = {
+ HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
+ HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
+ HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
-MODULE_ALIAS("snd-hda-codec-id:10134208");
-MODULE_ALIAS("snd-hda-codec-id:10134210");
-MODULE_ALIAS("snd-hda-codec-id:10134213");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
static struct hda_codec_driver cirrus_driver = {
- .preset = snd_hda_preset_cirrus,
+ .id = snd_hda_id_cirrus,
};
module_hda_codec_driver(cirrus_driver);
diff --git a/kernel/sound/pci/hda/patch_cmedia.c b/kernel/sound/pci/hda/patch_cmedia.c
index 617d9012e..1b2195dd2 100644
--- a/kernel/sound/pci/hda/patch_cmedia.c
+++ b/kernel/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:
@@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
- { .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
- { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
- { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+ HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
+ HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
+ HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:13f68888");
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
static struct hda_codec_driver cmedia_driver = {
- .preset = snd_hda_preset_cmedia,
+ .id = snd_hda_id_cmedia,
};
module_hda_codec_driver(cmedia_driver);
diff --git a/kernel/sound/pci/hda/patch_conexant.c b/kernel/sound/pci/hda/patch_conexant.c
index 06cc9d57b..ef198903c 100644
--- a/kernel/sound/pci/hda/patch_conexant.c
+++ b/kernel/sound/pci/hda/patch_conexant.c
@@ -819,6 +819,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
@@ -871,6 +872,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);
@@ -929,8 +931,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.
@@ -954,100 +954,45 @@ static int patch_conexant_auto(struct hda_codec *codec)
/*
*/
-static const struct hda_codec_preset snd_hda_preset_conexant[] = {
- { .id = 0x14f15045, .name = "CX20549 (Venice)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15066, .name = "CX20582 (Pebble)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15068, .name = "CX20584",
- .patch = patch_conexant_auto },
- { .id = 0x14f15069, .name = "CX20585",
- .patch = patch_conexant_auto },
- { .id = 0x14f1506c, .name = "CX20588",
- .patch = patch_conexant_auto },
- { .id = 0x14f1506e, .name = "CX20590",
- .patch = patch_conexant_auto },
- { .id = 0x14f15097, .name = "CX20631",
- .patch = patch_conexant_auto },
- { .id = 0x14f15098, .name = "CX20632",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a1, .name = "CX20641",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a2, .name = "CX20642",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ab, .name = "CX20651",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ac, .name = "CX20652",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b8, .name = "CX20664",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b9, .name = "CX20665",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f1, .name = "CX20721",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f2, .name = "CX20722",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f3, .name = "CX20723",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f4, .name = "CX20724",
- .patch = patch_conexant_auto },
- { .id = 0x14f1510f, .name = "CX20751/2",
- .patch = patch_conexant_auto },
- { .id = 0x14f15110, .name = "CX20751/2",
- .patch = patch_conexant_auto },
- { .id = 0x14f15111, .name = "CX20753/4",
- .patch = patch_conexant_auto },
- { .id = 0x14f15113, .name = "CX20755",
- .patch = patch_conexant_auto },
- { .id = 0x14f15114, .name = "CX20756",
- .patch = patch_conexant_auto },
- { .id = 0x14f15115, .name = "CX20757",
- .patch = patch_conexant_auto },
- { .id = 0x14f151d7, .name = "CX20952",
- .patch = patch_conexant_auto },
+static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f1506c");
-MODULE_ALIAS("snd-hda-codec-id:14f1506e");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
-MODULE_ALIAS("snd-hda-codec-id:14f150f1");
-MODULE_ALIAS("snd-hda-codec-id:14f150f2");
-MODULE_ALIAS("snd-hda-codec-id:14f150f3");
-MODULE_ALIAS("snd-hda-codec-id:14f150f4");
-MODULE_ALIAS("snd-hda-codec-id:14f1510f");
-MODULE_ALIAS("snd-hda-codec-id:14f15110");
-MODULE_ALIAS("snd-hda-codec-id:14f15111");
-MODULE_ALIAS("snd-hda-codec-id:14f15113");
-MODULE_ALIAS("snd-hda-codec-id:14f15114");
-MODULE_ALIAS("snd-hda-codec-id:14f15115");
-MODULE_ALIAS("snd-hda-codec-id:14f151d7");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
static struct hda_codec_driver conexant_driver = {
- .preset = snd_hda_preset_conexant,
+ .id = snd_hda_id_conexant,
};
module_hda_codec_driver(conexant_driver);
diff --git a/kernel/sound/pci/hda/patch_hdmi.c b/kernel/sound/pci/hda/patch_hdmi.c
index 225b78b4e..70c945603 100644
--- a/kernel/sound/pci/hda/patch_hdmi.c
+++ b/kernel/sound/pci/hda/patch_hdmi.c
@@ -37,6 +37,8 @@
#include <sound/jack.h>
#include <sound/asoundef.h>
#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_jack.h"
@@ -48,8 +50,9 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
#define is_haswell(codec) ((codec)->core.vendor_id == 0x80862807)
#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808)
#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809)
+#define is_broxton(codec) ((codec)->core.vendor_id == 0x8086280a)
#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \
- || is_skylake(codec))
+ || is_skylake(codec) || is_broxton(codec))
#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882)
#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883)
@@ -86,7 +89,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
};
@@ -144,6 +147,9 @@ struct hdmi_spec {
*/
struct hda_multi_out multiout;
struct hda_pcm_stream pcm_playback;
+
+ /* i915/powerwell (Haswell+/Valleyview+) specific */
+ struct i915_audio_component_audio_ops i915_audio_ops;
};
@@ -432,7 +438,8 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
eld = &per_pin->sink_eld;
mutex_lock(&per_pin->lock);
- if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+ eld->eld_size > ELD_MAX_SIZE) {
mutex_unlock(&per_pin->lock);
snd_BUG();
return -EINVAL;
@@ -548,7 +555,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)
{
@@ -591,8 +598,8 @@ 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);
+ if (!per_pin->codec->bus->shutdown) {
+ snd_info_free_entry(per_pin->proc_entry);
per_pin->proc_entry = NULL;
}
}
@@ -1177,7 +1184,7 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid)
static void jack_callback(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
- check_presence_and_report(codec, jack->tbl->nid);
+ check_presence_and_report(codec, jack->nid);
}
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -1770,6 +1777,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
return non_pcm;
}
+/* There is a fixed mapping between audio pin node and display port
+ * on current Intel platforms:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_pin2port(hda_nid_t pin_nid)
+{
+ return pin_nid - 4;
+}
/*
* HDMI callbacks
@@ -1786,6 +1803,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
int pin_idx = hinfo_to_pin_index(codec, hinfo);
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct i915_audio_component *acomp = codec->bus->core.audio_component;
bool non_pcm;
int pinctl;
@@ -1802,6 +1821,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
}
+ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+ /* Todo: add DP1.2 MST audio support later */
+ if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
+ acomp->ops->sync_audio_rate(acomp->dev,
+ intel_pin2port(pin_nid),
+ runtime->rate);
+
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock);
per_pin->channels = substream->runtime->channels;
@@ -2049,9 +2075,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;
@@ -2074,14 +2098,17 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
int pcmdev = get_pcm_rec(spec, pin_idx)->device;
+ bool phantom_jack;
if (pcmdev > 0)
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
- if (!is_jack_detectable(codec, per_pin->pin_nid))
+ phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
+ if (phantom_jack)
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,
+ phantom_jack);
}
static int generic_hdmi_build_controls(struct hda_codec *codec)
@@ -2193,6 +2220,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec;
int pin_idx;
+ if (is_haswell_plus(codec) || is_valleyview_plus(codec))
+ snd_hdac_i915_register_notifier(NULL);
+
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
@@ -2318,6 +2348,20 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
snd_hda_codec_set_power_to_all(codec, fg, power_state);
}
+static void intel_pin_eld_notify(void *audio_ptr, int port)
+{
+ struct hda_codec *codec = audio_ptr;
+ int pin_nid = port + 0x04;
+
+ /* skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume
+ */
+ if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
+ return;
+
+ check_presence_and_report(codec, pin_nid);
+}
+
static int patch_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
@@ -2335,8 +2379,22 @@ static int patch_generic_hdmi(struct hda_codec *codec)
intel_haswell_fixup_enable_dp12(codec);
}
- if (is_haswell_plus(codec) || is_valleyview_plus(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) ||
+ is_broxton(codec))
+ codec->core.link_power_control = 1;
+
+ if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
codec->depop_delay = 0;
+ spec->i915_audio_ops.audio_ptr = codec;
+ spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
+ snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+ }
if (hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
@@ -2349,6 +2407,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();
@@ -2530,7 +2592,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hdmi_spec *spec = codec->spec;
struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
- switch (codec->preset->id) {
+ switch (codec->preset->vendor_id) {
case 0x10de0002:
case 0x10de0003:
case 0x10de0005:
@@ -2848,7 +2910,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
snd_pcm_alt_chmaps, 8, 0, &chmap);
if (err < 0)
return err;
- switch (codec->preset->id) {
+ switch (codec->preset->vendor_id) {
case 0x10de0002:
case 0x10de0003:
case 0x10de0005:
@@ -2923,6 +2985,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
*/
@@ -3291,133 +3518,77 @@ static int patch_via_hdmi(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi },
+static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
/* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi },
-{ .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 = 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 },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
-{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
-{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
/* special ID for generic HDMI */
-{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0015");
-MODULE_ALIAS("snd-hda-codec-id:10de0016");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0028");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0051");
-MODULE_ALIAS("snd-hda-codec-id:10de0060");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de0070");
-MODULE_ALIAS("snd-hda-codec-id:10de0071");
-MODULE_ALIAS("snd-hda-codec-id:10de0072");
-MODULE_ALIAS("snd-hda-codec-id:10de007d");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:11069f80");
-MODULE_ALIAS("snd-hda-codec-id:11069f81");
-MODULE_ALIAS("snd-hda-codec-id:11069f84");
-MODULE_ALIAS("snd-hda-codec-id:11069f85");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80862806");
-MODULE_ALIAS("snd-hda-codec-id:80862807");
-MODULE_ALIAS("snd-hda-codec-id:80862808");
-MODULE_ALIAS("snd-hda-codec-id:80862809");
-MODULE_ALIAS("snd-hda-codec-id:80862880");
-MODULE_ALIAS("snd-hda-codec-id:80862882");
-MODULE_ALIAS("snd-hda-codec-id:80862883");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HDMI HD-audio codec");
@@ -3426,7 +3597,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
static struct hda_codec_driver hdmi_driver = {
- .preset = snd_hda_preset_hdmi,
+ .id = snd_hda_id_hdmi,
};
module_hda_codec_driver(hdmi_driver);
diff --git a/kernel/sound/pci/hda/patch_realtek.c b/kernel/sound/pci/hda/patch_realtek.c
index 6fe862594..c2430b36e 100644
--- a/kernel/sound/pci/hda/patch_realtek.c
+++ b/kernel/sound/pci/hda/patch_realtek.c
@@ -67,6 +67,10 @@ enum {
ALC_HEADSET_TYPE_OMTP,
};
+enum {
+ ALC_KEY_MICMUTE_INDEX,
+};
+
struct alc_customize_define {
unsigned int sku_cfg;
unsigned char port_connectivity;
@@ -111,6 +115,7 @@ struct alc_spec {
void (*power_hook)(struct hda_codec *codec);
#endif
void (*shutup)(struct hda_codec *codec);
+ void (*reboot_notify)(struct hda_codec *codec);
int init_amp;
int codec_variant; /* flag for other variants */
@@ -122,6 +127,7 @@ struct alc_spec {
unsigned int pll_coef_idx, pll_coef_bit;
unsigned int coef0;
struct input_dev *kb_dev;
+ u8 alc_mute_keycode_map[1];
};
/*
@@ -276,7 +282,7 @@ static void alc_update_knob_master(struct hda_codec *codec,
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return;
- val = snd_hda_codec_read(codec, jack->tbl->nid, 0,
+ val = snd_hda_codec_read(codec, jack->nid, 0,
AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
val &= HDA_AMP_VOLMASK;
uctl->value.integer.value[0] = val;
@@ -321,6 +327,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0292:
alc_update_coef_idx(codec, 0x4, 1<<15, 0);
break;
+ case 0x10ec0225:
case 0x10ec0233:
case 0x10ec0255:
case 0x10ec0256:
@@ -773,6 +780,25 @@ static inline void alc_shutup(struct hda_codec *codec)
snd_hda_shutup_pins(codec);
}
+static void alc_reboot_notify(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec && spec->reboot_notify)
+ spec->reboot_notify(codec);
+ else
+ alc_shutup(codec);
+}
+
+/* power down codec to D3 at reboot/shutdown; set as reboot_notify ops */
+static void alc_d3_at_reboot(struct hda_codec *codec)
+{
+ snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ msleep(10);
+}
+
#define alc_free snd_hda_gen_free
#ifdef CONFIG_PM
@@ -818,21 +844,11 @@ static const struct hda_codec_ops alc_patch_ops = {
.suspend = alc_suspend,
.check_power_status = snd_hda_gen_check_power_status,
#endif
- .reboot_notify = alc_shutup,
+ .reboot_notify = alc_reboot_notify,
};
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup(name, GFP_KERNEL);
- if (!codec->core.chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return 0;
-}
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
/*
* Rename codecs appropriately from COEF value or subvendor id
@@ -885,6 +901,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
{ 0x10ec0899, 0x1028, 0, "ALC3861" },
{ 0x10ec0298, 0x1028, 0, "ALC3266" },
{ 0x10ec0256, 0x1028, 0, "ALC3246" },
+ { 0x10ec0225, 0x1028, 0, "ALC3253" },
{ 0x10ec0670, 0x1025, 0, "ALC669X" },
{ 0x10ec0676, 0x1025, 0, "ALC679X" },
{ 0x10ec0282, 0x1043, 0, "ALC3229" },
@@ -1003,6 +1020,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 +1465,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 +1479,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 +1715,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 +1729,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;
@@ -1767,6 +1782,7 @@ enum {
ALC889_FIXUP_MBA11_VREF,
ALC889_FIXUP_MBA21_VREF,
ALC889_FIXUP_MP11_VREF,
+ ALC889_FIXUP_MP41_VREF,
ALC882_FIXUP_INV_DMIC,
ALC882_FIXUP_NO_PRIMARY_HP,
ALC887_FIXUP_ASUS_BASS,
@@ -1854,7 +1870,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- static hda_nid_t nids[2] = { 0x14, 0x15 };
+ static hda_nid_t nids[3] = { 0x14, 0x15, 0x19 };
int i;
if (action != HDA_FIXUP_ACT_INIT)
@@ -2142,6 +2158,12 @@ static const struct hda_fixup alc882_fixups[] = {
.chained = true,
.chain_id = ALC885_FIXUP_MACPRO_GPIO,
},
+ [ALC889_FIXUP_MP41_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
[ALC882_FIXUP_INV_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic,
@@ -2201,6 +2223,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP),
/* All Apple entries are in codec SSIDs */
SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
@@ -2220,7 +2243,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF),
- SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 5,1", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF),
SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF),
@@ -2299,8 +2322,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 +2457,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 +2484,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 +2587,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 +2610,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;
@@ -2627,6 +2646,7 @@ enum {
ALC269_TYPE_ALC298,
ALC269_TYPE_ALC255,
ALC269_TYPE_ALC256,
+ ALC269_TYPE_ALC225,
};
/*
@@ -2656,6 +2676,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC298:
case ALC269_TYPE_ALC255:
case ALC269_TYPE_ALC256:
+ case ALC269_TYPE_ALC225:
ssids = alc269_ssids;
break;
default:
@@ -3443,12 +3464,43 @@ static void gpio2_mic_hotkey_event(struct hda_codec *codec,
/* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
send both key on and key off event for every interrupt. */
- input_report_key(spec->kb_dev, KEY_MICMUTE, 1);
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1);
input_sync(spec->kb_dev);
- input_report_key(spec->kb_dev, KEY_MICMUTE, 0);
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0);
input_sync(spec->kb_dev);
}
+static int alc_register_micmute_input_device(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ spec->kb_dev = input_allocate_device();
+ if (!spec->kb_dev) {
+ codec_err(codec, "Out of memory (input_allocate_device)\n");
+ return -ENOMEM;
+ }
+
+ spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;
+
+ spec->kb_dev->name = "Microphone Mute Button";
+ spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
+ spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]);
+ spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map);
+ spec->kb_dev->keycode = spec->alc_mute_keycode_map;
+ for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++)
+ set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit);
+
+ if (input_register_device(spec->kb_dev)) {
+ codec_err(codec, "input_register_device failed\n");
+ input_free_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -3466,20 +3518,8 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->kb_dev = input_allocate_device();
- if (!spec->kb_dev) {
- codec_err(codec, "Out of memory (input_allocate_device)\n");
- return;
- }
- spec->kb_dev->name = "Microphone Mute Button";
- spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
- spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE);
- if (input_register_device(spec->kb_dev)) {
- codec_err(codec, "input_register_device failed\n");
- input_free_device(spec->kb_dev);
- spec->kb_dev = NULL;
+ if (alc_register_micmute_input_device(codec) != 0)
return;
- }
snd_hda_add_verbs(codec, gpio_init);
snd_hda_codec_write_cache(codec, codec->core.afg, 0,
@@ -3509,6 +3549,47 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
}
}
+static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Line2 = mic mute hotkey
+ GPIO2 = mic mute LED */
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
+ {}
+ };
+
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ snd_hda_add_verbs(codec, gpio_init);
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ gpio2_mic_hotkey_event);
+
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mic_led_mask = 0x04;
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -3574,6 +3655,16 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
WRITE_COEF(0xb7, 0x802b),
{}
};
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x4a, 1<<8, 0),
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0),
+ UPDATE_COEF(0x63, 3<<14, 3<<14),
+ UPDATE_COEF(0x4a, 3<<4, 2<<4),
+ UPDATE_COEF(0x4a, 3<<10, 3<<10),
+ UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
+ UPDATE_COEF(0x4a, 3<<10, 0),
+ {}
+ };
switch (codec->core.vendor_id) {
case 0x10ec0255:
@@ -3586,6 +3677,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:
@@ -3597,6 +3689,9 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
case 0x10ec0668:
alc_process_coef_fw(codec, coef0668);
break;
+ case 0x10ec0225:
+ alc_process_coef_fw(codec, coef0225);
+ break;
}
codec_dbg(codec, "Headset jack set to unplugged mode.\n");
}
@@ -3642,6 +3737,13 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
UPDATE_COEF(0xc3, 0, 1<<12),
{}
};
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14),
+ UPDATE_COEF(0x4a, 3<<4, 2<<4),
+ UPDATE_COEF(0x63, 3<<14, 0),
+ {}
+ };
+
switch (codec->core.vendor_id) {
case 0x10ec0255:
@@ -3660,6 +3762,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);
@@ -3686,6 +3789,12 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
alc_process_coef_fw(codec, coef0688);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break;
+ case 0x10ec0225:
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0225);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
}
codec_dbg(codec, "Headset jack set to mic-in mode.\n");
}
@@ -3743,6 +3852,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:
@@ -3797,6 +3907,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
WRITE_COEF(0xc3, 0x0000),
{}
};
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
+ UPDATE_COEF(0x49, 1<<8, 1<<8),
+ UPDATE_COEF(0x4a, 7<<6, 7<<6),
+ UPDATE_COEF(0x4a, 3<<4, 3<<4),
+ {}
+ };
switch (codec->core.vendor_id) {
case 0x10ec0255:
@@ -3807,6 +3924,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);
@@ -3822,6 +3942,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
case 0x10ec0668:
alc_process_coef_fw(codec, coef0688);
break;
+ case 0x10ec0225:
+ alc_process_coef_fw(codec, coef0225);
+ break;
}
codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
}
@@ -3865,6 +3988,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
WRITE_COEF(0xc3, 0x0000),
{}
};
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10),
+ UPDATE_COEF(0x49, 1<<8, 1<<8),
+ UPDATE_COEF(0x4a, 7<<6, 7<<6),
+ UPDATE_COEF(0x4a, 3<<4, 3<<4),
+ {}
+ };
switch (codec->core.vendor_id) {
case 0x10ec0255:
@@ -3875,6 +4005,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);
@@ -3890,6 +4023,9 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
case 0x10ec0668:
alc_process_coef_fw(codec, coef0688);
break;
+ case 0x10ec0225:
+ alc_process_coef_fw(codec, coef0225);
+ break;
}
codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
}
@@ -3921,6 +4057,11 @@ static void alc_determine_headset_type(struct hda_codec *codec)
WRITE_COEF(0xc3, 0x0c00),
{}
};
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
+ UPDATE_COEF(0x49, 1<<8, 1<<8),
+ {}
+ };
switch (codec->core.vendor_id) {
case 0x10ec0255:
@@ -3937,6 +4078,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);
@@ -3962,6 +4106,12 @@ static void alc_determine_headset_type(struct hda_codec *codec)
val = alc_read_coef_idx(codec, 0xbe);
is_ctia = (val & 0x1c02) == 0x1c02;
break;
+ case 0x10ec0225:
+ alc_process_coef_fw(codec, coef0225);
+ msleep(800);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ break;
}
codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
@@ -4182,6 +4332,26 @@ static void alc_fixup_disable_aamix(struct hda_codec *codec,
}
}
+/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */
+static void alc_fixup_tpt440_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x16, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->shutup = alc_no_shutup; /* reduce click noise */
+ spec->reboot_notify = alc_d3_at_reboot; /* reduce noise */
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ codec->power_save_node = 0; /* avoid click noises */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
static void alc_shutup_dell_xps13(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -4435,6 +4605,55 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec,
}
}
+/* Hook to update amp GPIO4 for automute */
+static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_gen_hp_automute(codec, jack);
+ /* mute_led_polarity is set to 0, so we pass inverted value here */
+ alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present);
+}
+
+/* Manage GPIOs for HP EliteBook Folio 9480m.
+ *
+ * GPIO4 is the headphone amplifier power control
+ * GPIO3 is the audio output mute indicator LED
+ */
+
+static void alc280_fixup_hp_9480m(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* Set the hooks to turn the headphone amp on/off
+ * as needed
+ */
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
+ spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook;
+
+ /* The GPIOs are currently off */
+ spec->gpio_led = 0;
+
+ /* GPIO3 is connected to the output mute LED,
+ * high is on, low is off
+ */
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x08;
+
+ /* Initialize GPIO configuration */
+ snd_hda_add_verbs(codec, gpio_init);
+ }
+}
+
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
@@ -4501,13 +4720,14 @@ enum {
ALC290_FIXUP_SUBWOOFER,
ALC290_FIXUP_SUBWOOFER_HSJACK,
ALC269_FIXUP_THINKPAD_ACPI,
+ ALC269_FIXUP_DMIC_THINKPAD_ACPI,
ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE,
ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_FIXUP_TPT440_DOCK,
- ALC292_FIXUP_TPT440_DOCK2,
+ ALC292_FIXUP_TPT440,
ALC283_FIXUP_BXBT2807_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
@@ -4515,6 +4735,7 @@ enum {
ALC286_FIXUP_HP_GPIO_LED,
ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY,
ALC280_FIXUP_HP_DOCK_PINS,
+ ALC280_FIXUP_HP_9480M,
ALC288_FIXUP_DELL_HEADSET_MODE,
ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC288_FIXUP_DELL_XPS_13_GPIO6,
@@ -4522,6 +4743,13 @@ enum {
ALC288_FIXUP_DISABLE_AAMIX,
ALC292_FIXUP_DELL_E7X,
ALC292_FIXUP_DISABLE_AAMIX,
+ ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK,
+ ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC275_FIXUP_DELL_XPS,
+ ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
+ ALC293_FIXUP_LENOVO_SPK_NOISE,
+ ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ ALC255_FIXUP_DELL_SPK_NOISE,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -4931,6 +5159,12 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = hda_fixup_thinkpad_acpi,
},
+ [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
[ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -4972,19 +5206,15 @@ static const struct hda_fixup alc269_fixups[] = {
},
[ALC292_FIXUP_TPT440_DOCK] = {
.type = HDA_FIXUP_FUNC,
- .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ .v.func = alc_fixup_tpt440_dock,
.chained = true,
- .chain_id = ALC292_FIXUP_TPT440_DOCK2
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
- [ALC292_FIXUP_TPT440_DOCK2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x16, 0x21211010 }, /* dock headphone */
- { 0x19, 0x21a11010 }, /* dock mic */
- { }
- },
+ [ALC292_FIXUP_TPT440] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
.chained = true,
- .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ .chain_id = ALC292_FIXUP_TPT440_DOCK,
},
[ALC283_FIXUP_BXBT2807_MIC] = {
.type = HDA_FIXUP_PINS,
@@ -5036,6 +5266,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC280_FIXUP_HP_GPIO4
},
+ [ALC280_FIXUP_HP_9480M] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_9480m,
+ },
[ALC288_FIXUP_DELL_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_dell_alc288,
@@ -5081,12 +5315,66 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE
},
+ [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
[ALC292_FIXUP_DELL_E7X] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_dell_xps13,
.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
+ },
+ [ALC275_FIXUP_DELL_XPS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00c0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x30},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00b1},
+ {}
+ }
+ },
+ [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x36},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x1737},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC293_FIXUP_LENOVO_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_line2_mic_hotkey,
+ },
+ [ALC255_FIXUP_DELL_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5098,9 +5386,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
+ SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
+ SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X),
SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X),
SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X),
SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
@@ -5109,6 +5402,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X),
SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X),
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -5118,11 +5412,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
+ SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -5150,6 +5446,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M),
SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
/* ALC290 */
@@ -5220,13 +5517,19 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440),
SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -5236,6 +5539,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
@@ -5307,6 +5611,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"},
@@ -5314,347 +5620,219 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
{.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
{.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
+ {.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
{}
};
-
-#define ALC255_STANDARD_PINS \
- {0x18, 0x411111f0}, \
- {0x19, 0x411111f0}, \
- {0x1a, 0x411111f0}, \
- {0x1b, 0x411111f0}, \
- {0x1e, 0x411111f0}
+#define ALC225_STANDARD_PINS \
+ {0x12, 0xb7a60130}, \
+ {0x21, 0x04211020}
#define ALC256_STANDARD_PINS \
{0x12, 0x90a60140}, \
{0x14, 0x90170110}, \
- {0x19, 0x411111f0}, \
- {0x1a, 0x411111f0}, \
- {0x1b, 0x411111f0}, \
- {0x1d, 0x40700001}, \
- {0x1e, 0x411111f0}, \
{0x21, 0x02211020}
#define ALC282_STANDARD_PINS \
- {0x14, 0x90170110}, \
- {0x18, 0x411111f0}, \
- {0x1a, 0x411111f0}, \
- {0x1b, 0x411111f0}, \
- {0x1e, 0x411111f0}
-
-#define ALC288_STANDARD_PINS \
- {0x17, 0x411111f0}, \
- {0x18, 0x411111f0}, \
- {0x19, 0x411111f0}, \
- {0x1a, 0x411111f0}, \
- {0x1e, 0x411111f0}
+ {0x14, 0x90170110}
#define ALC290_STANDARD_PINS \
- {0x12, 0x99a30130}, \
- {0x13, 0x40000000}, \
- {0x16, 0x411111f0}, \
- {0x17, 0x411111f0}, \
- {0x19, 0x411111f0}, \
- {0x1b, 0x411111f0}, \
- {0x1e, 0x411111f0}
+ {0x12, 0x99a30130}
#define ALC292_STANDARD_PINS \
{0x14, 0x90170110}, \
- {0x15, 0x0221401f}, \
- {0x1a, 0x411111f0}, \
- {0x1b, 0x411111f0}, \
- {0x1d, 0x40700001}, \
- {0x1e, 0x411111f0}
+ {0x15, 0x0221401f}
+
+#define ALC298_STANDARD_PINS \
+ {0x12, 0x90a60130}, \
+ {0x21, 0x03211020}
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x14, 0x901701b0}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
- {0x12, 0x40300000},
{0x14, 0x90170110},
- {0x17, 0x411111f0},
- {0x1d, 0x40538029},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60140},
{0x14, 0x90170110},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60160},
{0x14, 0x90170120},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- {0x12, 0x40000000},
{0x14, 0x90170130},
- {0x17, 0x411111f0},
- {0x18, 0x411111f0},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
{0x1b, 0x01014020},
- {0x1d, 0x4054c029},
- {0x1e, 0x411111f0},
{0x21, 0x0221103f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170150},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221105f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x17, 0x90170140},
- {0x18, 0x40000000},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x41163b05},
- {0x1e, 0x411111f0},
{0x21, 0x0321102f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60160},
{0x14, 0x90170130},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211040}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60160},
{0x14, 0x90170140},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211050}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60170},
{0x14, 0x90170120},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60170},
{0x14, 0x90170130},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211040}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
+ {0x12, 0x90a60170},
+ {0x14, 0x90171130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60170},
{0x14, 0x90170140},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211050}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60180},
{0x14, 0x90170130},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211040}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC255_STANDARD_PINS,
{0x12, 0x90a60160},
{0x14, 0x90170120},
- {0x17, 0x40000000},
- {0x1d, 0x40700001},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC256_STANDARD_PINS,
- {0x13, 0x40000000}),
- SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC256_STANDARD_PINS,
- {0x13, 0x411111f0}),
+ ALC256_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
- {0x13, 0x40000000},
{0x14, 0x90170110},
{0x15, 0x0421101f},
- {0x16, 0x411111f0},
- {0x17, 0x411111f0},
- {0x18, 0x411111f0},
- {0x19, 0x411111f0},
- {0x1a, 0x04a11020},
- {0x1b, 0x411111f0},
- {0x1d, 0x40748605},
- {0x1e, 0x411111f0}),
+ {0x1a, 0x04a11020}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
{0x12, 0x90a60140},
- {0x13, 0x40000000},
{0x14, 0x90170110},
{0x15, 0x0421101f},
- {0x16, 0x411111f0},
- {0x17, 0x411111f0},
{0x18, 0x02811030},
- {0x19, 0x411111f0},
{0x1a, 0x04a1103f},
- {0x1b, 0x02011020},
- {0x1d, 0x40700001},
- {0x1e, 0x411111f0}),
+ {0x1b, 0x02011020}),
SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC282_STANDARD_PINS,
{0x12, 0x99a30130},
- {0x17, 0x40000000},
{0x19, 0x03a11020},
- {0x1d, 0x40f41905},
{0x21, 0x0321101f}),
SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC282_STANDARD_PINS,
{0x12, 0x99a30130},
- {0x17, 0x40020008},
{0x19, 0x03a11020},
- {0x1d, 0x40e00001},
{0x21, 0x03211040}),
SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC282_STANDARD_PINS,
{0x12, 0x99a30130},
- {0x17, 0x40000000},
- {0x19, 0x03a11030},
- {0x1d, 0x40e00001},
- {0x21, 0x03211020}),
- SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
- ALC282_STANDARD_PINS,
- {0x12, 0x99a30130},
- {0x17, 0x40000000},
{0x19, 0x03a11030},
- {0x1d, 0x40f00001},
{0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC282_STANDARD_PINS,
{0x12, 0x99a30130},
- {0x17, 0x40000000},
{0x19, 0x04a11020},
- {0x1d, 0x40f00001},
{0x21, 0x0421101f}),
- SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
- ALC282_STANDARD_PINS,
- {0x12, 0x99a30130},
- {0x17, 0x40000000},
- {0x19, 0x03a11030},
- {0x1d, 0x40f00001},
- {0x21, 0x04211020}),
SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED,
ALC282_STANDARD_PINS,
{0x12, 0x90a60140},
- {0x17, 0x40000000},
{0x19, 0x04a11030},
- {0x1d, 0x40f00001},
{0x21, 0x04211020}),
SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC282_STANDARD_PINS,
{0x12, 0x90a60130},
- {0x17, 0x40020008},
- {0x19, 0x411111f0},
- {0x1d, 0x40e00001},
{0x21, 0x0321101f}),
SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
- {0x17, 0x40000000},
- {0x18, 0x411111f0},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x40700001},
- {0x1e, 0x411111f0},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC282_STANDARD_PINS,
{0x12, 0x90a60130},
- {0x17, 0x40020008},
{0x19, 0x03a11020},
- {0x1d, 0x40e00001},
{0x21, 0x0321101f}),
SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6,
- ALC288_STANDARD_PINS,
{0x12, 0x90a60120},
- {0x13, 0x40000000},
{0x14, 0x90170110},
- {0x1d, 0x4076832d},
{0x21, 0x0321101f}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
- {0x14, 0x411111f0},
{0x15, 0x04211040},
{0x18, 0x90170112},
- {0x1a, 0x04a11020},
- {0x1d, 0x4075812d}),
+ {0x1a, 0x04a11020}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
- {0x14, 0x411111f0},
{0x15, 0x04211040},
{0x18, 0x90170110},
- {0x1a, 0x04a11020},
- {0x1d, 0x4075812d}),
+ {0x1a, 0x04a11020}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
- {0x14, 0x411111f0},
{0x15, 0x0421101f},
- {0x18, 0x411111f0},
- {0x1a, 0x04a11020},
- {0x1d, 0x4075812d}),
+ {0x1a, 0x04a11020}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
- {0x14, 0x411111f0},
{0x15, 0x04211020},
- {0x18, 0x411111f0},
- {0x1a, 0x04a11040},
- {0x1d, 0x4076a12d}),
+ {0x1a, 0x04a11040}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
{0x14, 0x90170110},
{0x15, 0x04211020},
- {0x18, 0x411111f0},
- {0x1a, 0x04a11040},
- {0x1d, 0x4076a12d}),
+ {0x1a, 0x04a11040}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
{0x14, 0x90170110},
{0x15, 0x04211020},
- {0x18, 0x411111f0},
- {0x1a, 0x04a11020},
- {0x1d, 0x4076a12d}),
+ {0x1a, 0x04a11020}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
{0x14, 0x90170110},
{0x15, 0x0421101f},
- {0x18, 0x411111f0},
- {0x1a, 0x04a11020},
- {0x1d, 0x4075812d}),
+ {0x1a, 0x04a11020}),
SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x12, 0x90a60140},
- {0x13, 0x411111f0},
{0x16, 0x01014020},
- {0x18, 0x411111f0},
{0x19, 0x01a19030}),
SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x12, 0x90a60140},
- {0x13, 0x411111f0},
{0x16, 0x01014020},
{0x18, 0x02a19031},
{0x19, 0x01a1903e}),
SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
- {0x12, 0x90a60140},
- {0x13, 0x411111f0},
- {0x16, 0x411111f0},
- {0x18, 0x411111f0},
- {0x19, 0x411111f0}),
+ {0x12, 0x90a60140}),
SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
- {0x12, 0x40000000},
{0x13, 0x90a60140},
{0x16, 0x21014020},
- {0x18, 0x411111f0},
{0x19, 0x21a19030}),
SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
- {0x12, 0x40000000},
- {0x13, 0x90a60140},
- {0x16, 0x411111f0},
- {0x18, 0x411111f0},
- {0x19, 0x411111f0}),
+ {0x13, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170140}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170150}),
{}
};
@@ -5713,6 +5891,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);
@@ -5794,6 +5978,9 @@ static int patch_alc269(struct hda_codec *codec)
spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
break;
+ case 0x10ec0225:
+ spec->codec_variant = ALC269_TYPE_ALC225;
+ break;
}
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@@ -5809,15 +5996,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;
@@ -5933,6 +6111,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);
@@ -5944,11 +6126,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;
@@ -6025,6 +6202,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);
@@ -6036,10 +6215,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;
@@ -6448,6 +6623,7 @@ static const struct hda_fixup alc662_fixups[] = {
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
@@ -6465,6 +6641,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
@@ -6554,77 +6731,33 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
- {0x12, 0x4004c000},
{0x14, 0x01014010},
- {0x15, 0x411111f0},
- {0x16, 0x411111f0},
{0x18, 0x01a19020},
- {0x19, 0x411111f0},
{0x1a, 0x0181302f},
- {0x1b, 0x0221401f},
- {0x1c, 0x411111f0},
- {0x1d, 0x4054c601},
- {0x1e, 0x411111f0}),
+ {0x1b, 0x0221401f}),
SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
{0x12, 0x99a30130},
{0x14, 0x90170110},
{0x15, 0x0321101f},
- {0x16, 0x03011020},
- {0x18, 0x40000008},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x41000001},
- {0x1e, 0x411111f0},
- {0x1f, 0x411111f0}),
+ {0x16, 0x03011020}),
SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
{0x12, 0x99a30140},
{0x14, 0x90170110},
{0x15, 0x0321101f},
- {0x16, 0x03011020},
- {0x18, 0x40000008},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x41000001},
- {0x1e, 0x411111f0},
- {0x1f, 0x411111f0}),
+ {0x16, 0x03011020}),
SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
{0x12, 0x99a30150},
{0x14, 0x90170110},
{0x15, 0x0321101f},
- {0x16, 0x03011020},
- {0x18, 0x40000008},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x41000001},
- {0x1e, 0x411111f0},
- {0x1f, 0x411111f0}),
+ {0x16, 0x03011020}),
SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
- {0x12, 0x411111f0},
{0x14, 0x90170110},
{0x15, 0x0321101f},
- {0x16, 0x03011020},
- {0x18, 0x40000008},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x41000001},
- {0x1e, 0x411111f0},
- {0x1f, 0x411111f0}),
+ {0x16, 0x03011020}),
SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE,
{0x12, 0x90a60130},
{0x14, 0x90170110},
- {0x15, 0x0321101f},
- {0x16, 0x40000000},
- {0x18, 0x411111f0},
- {0x19, 0x411111f0},
- {0x1a, 0x411111f0},
- {0x1b, 0x411111f0},
- {0x1d, 0x40d6832d},
- {0x1e, 0x411111f0},
- {0x1f, 0x411111f0}),
+ {0x15, 0x0321101f}),
{}
};
@@ -6641,6 +6774,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;
@@ -6692,9 +6827,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;
@@ -6731,86 +6863,77 @@ static int patch_alc680(struct hda_codec *codec)
return err;
}
- codec->patch_ops = alc_patch_ops;
-
return 0;
}
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_realtek[] = {
- { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
- { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
- { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
- { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
- { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
- { .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 },
- { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
- { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
- { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
- { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
- { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
- { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
- { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
- { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
- { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
- { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
- { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
- { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
- { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
- { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
- { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
- { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
- { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
- { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
- { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
- { .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 },
- { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
- .patch = patch_alc861 },
- { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
- { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
- .patch = patch_alc882 },
- { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
- .patch = patch_alc662 },
- { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
- .patch = patch_alc662 },
- { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
- { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
- { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
- { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
- { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
- { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
- { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
- { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
- { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
- { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
- { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
- .patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
- { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
- { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
- { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
- { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
+static const struct hda_device_id snd_hda_id_realtek[] = {
+ HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
+ HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
+ HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
+ HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
+ HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+ HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
+ HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek HD-audio codec");
static struct hda_codec_driver realtek_driver = {
- .preset = snd_hda_preset_realtek,
+ .id = snd_hda_id_realtek,
};
module_hda_codec_driver(realtek_driver);
diff --git a/kernel/sound/pci/hda/patch_si3054.c b/kernel/sound/pci/hda/patch_si3054.c
index 5104bebb2..ffda38c45 100644
--- a/kernel/sound/pci/hda/patch_si3054.c
+++ b/kernel/sound/pci/hda/patch_si3054.c
@@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_si3054[] = {
- { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+ HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
/* VIA HDA on Clevo m540 */
- { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
/* Asus A8J Modem (SM56) */
- { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
/* LG LW20 modem */
- { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
{}
};
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
static struct hda_codec_driver si3054_driver = {
- .preset = snd_hda_preset_si3054,
+ .id = snd_hda_id_si3054,
};
module_hda_codec_driver(si3054_driver);
diff --git a/kernel/sound/pci/hda/patch_sigmatel.c b/kernel/sound/pci/hda/patch_sigmatel.c
index 25f0f45e6..37b70f8e8 100644
--- a/kernel/sound/pci/hda/patch_sigmatel.c
+++ b/kernel/sound/pci/hda/patch_sigmatel.c
@@ -493,9 +493,9 @@ static void jack_update_power(struct hda_codec *codec,
if (!spec->num_pwrs)
return;
- if (jack && jack->tbl->nid) {
- stac_toggle_power_map(codec, jack->tbl->nid,
- snd_hda_jack_detect(codec, jack->tbl->nid),
+ if (jack && jack->nid) {
+ stac_toggle_power_map(codec, jack->nid,
+ snd_hda_jack_detect(codec, jack->nid),
true);
return;
}
@@ -702,6 +702,7 @@ static bool hp_bnb2011_with_dock(struct hda_codec *codec)
static bool hp_blike_system(u32 subsystem_id)
{
switch (subsystem_id) {
+ case 0x103c1473: /* HP ProBook 6550b */
case 0x103c1520:
case 0x103c1521:
case 0x103c1523:
@@ -3109,6 +3110,29 @@ static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec,
spec->gpio_led = 0x08;
}
+static bool is_hp_output(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* count line-out, too, as BIOS sets often so */
+ return get_defcfg_connect(pin_cfg) != AC_JACK_PORT_NONE &&
+ (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT);
+}
+
+static void fixup_hp_headphone(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into slaved HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) |
+ (AC_JACK_HP_OUT << AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC | AC_DEFCFG_SEQUENCE))) |
+ 0x1f;
+ snd_hda_codec_set_pincfg(codec, pin, pin_cfg);
+}
static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
@@ -3118,22 +3142,12 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
- if (hp_blike_system(codec->core.subsystem_id)) {
- unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
- if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
- get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
- get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
- /* It was changed in the BIOS to just satisfy MS DTM.
- * Lets turn it back into slaved HP
- */
- pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
- | (AC_JACK_HP_OUT <<
- AC_DEFCFG_DEVICE_SHIFT);
- pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
- | AC_DEFCFG_SEQUENCE)))
- | 0x1f;
- snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
- }
+ /* when both output A and F are assigned, these are supposedly
+ * dock and built-in headphones; fix both pin configs
+ */
+ if (is_hp_output(codec, 0x0a) && is_hp_output(codec, 0x0f)) {
+ fixup_hp_headphone(codec, 0x0a);
+ fixup_hp_headphone(codec, 0x0f);
}
if (find_mute_led_cfg(codec, 1))
@@ -4363,7 +4377,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)
{
@@ -4443,6 +4457,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;
}
@@ -4459,7 +4474,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);
@@ -4492,8 +4506,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,
@@ -4522,7 +4534,11 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
return err;
spec = codec->spec;
- codec->power_save_node = 1;
+ /* enable power_save_node only for new 92HD89xx chips, as it causes
+ * click noises on old 92HD73xx chips.
+ */
+ if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670)
+ codec->power_save_node = 1;
spec->linear_tone_beep = 0;
spec->gen.mixer_nid = 0x1d;
spec->have_spdif_mux = 1;
@@ -4563,8 +4579,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);
@@ -4640,8 +4654,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,
@@ -4690,8 +4702,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);
@@ -4730,8 +4740,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;
@@ -4810,8 +4818,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 */
@@ -4867,8 +4873,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);
@@ -4930,8 +4934,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);
@@ -5003,8 +5005,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,
@@ -5026,121 +5026,119 @@ static int patch_stac9872(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
- { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
- { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
- { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
- { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
- { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
- { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
- { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
- { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
- { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
- { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
- { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
- { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
- { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
- { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
- { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
- { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
- { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
- { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
- { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
- { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
- { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
- { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
- { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
- /* The following does not take into account .id=0x83847661 when subsys =
- * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
- * currently not fully supported.
- */
- { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
- { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
- { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
- { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
- { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
- { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
- { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
- { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
- { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
- { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
- { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
- { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+ HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
+ HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
+ /* The following does not take into account .id=0x83847661 when subsys =
+ * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ * currently not fully supported.
+ */
+ HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
+ HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
static struct hda_codec_driver sigmatel_driver = {
- .preset = snd_hda_preset_sigmatel,
+ .id = snd_hda_id_sigmatel,
};
module_hda_codec_driver(sigmatel_driver);
diff --git a/kernel/sound/pci/hda/patch_via.c b/kernel/sound/pci/hda/patch_via.c
index 0baeecc22..fc30d1e8a 100644
--- a/kernel/sound/pci/hda/patch_via.c
+++ b/kernel/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;
}
@@ -483,7 +486,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,
@@ -663,6 +665,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 */
@@ -691,10 +696,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;
@@ -719,8 +720,6 @@ static int patch_vt1709(struct hda_codec *codec)
return err;
}
- codec->patch_ops = via_patch_ops;
-
return 0;
}
@@ -747,7 +746,6 @@ static int patch_vt1708B(struct hda_codec *codec)
return err;
}
- codec->patch_ops = via_patch_ops;
return 0;
}
@@ -787,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec)
override_mic_boost(codec, 0x1e, 0, 3, 40);
/* correct names for VT1708BCE */
- if (get_codec_type(codec) == VT1708BCE) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername),
- "%s %s", codec->core.vendor_name, codec->core.chip_name);
- }
+ if (get_codec_type(codec) == VT1708BCE)
+ snd_hda_codec_set_name(codec, "VT1708BCE");
/* correct names for VT1705 */
- if (codec->core.vendor_id == 0x11064397) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL);
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername),
- "%s %s", codec->core.vendor_name, codec->core.chip_name);
- }
+ if (codec->core.vendor_id == 0x11064397)
+ snd_hda_codec_set_name(codec, "VT1705");
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
@@ -812,7 +800,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;
}
@@ -854,7 +841,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;
}
@@ -927,7 +913,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;
}
@@ -1027,7 +1012,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;
}
@@ -1135,7 +1119,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;
}
@@ -1174,7 +1157,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;
}
@@ -1212,116 +1194,70 @@ 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;
}
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_via[] = {
- { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106e710, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e711, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e712, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e713, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e714, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e715, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e716, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e717, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e720, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e721, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e722, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e723, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e724, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e725, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e726, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e727, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x11060397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11061397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11062397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11063397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VT1705",
- .patch = patch_vt1708S},
- { .id = 0x11065397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11066397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11067397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11060398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11061398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11062398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11063398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11064398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11065398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11066398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11067398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11060428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11064428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11060441, .name = "VT2020",
- .patch = patch_vt1718S},
- { .id = 0x11064441, .name = "VT1828S",
- .patch = patch_vt1718S},
- { .id = 0x11060433, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x1106a721, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
- { .id = 0x11060440, .name = "VT1818S",
- .patch = patch_vt1708S},
- { .id = 0x11060446, .name = "VT1802",
- .patch = patch_vt2002P},
- { .id = 0x11068446, .name = "VT1802",
- .patch = patch_vt2002P},
- { .id = 0x11064760, .name = "VT1705CF",
- .patch = patch_vt3476},
- { .id = 0x11064761, .name = "VT1708SCE",
- .patch = patch_vt3476},
- { .id = 0x11064762, .name = "VT1808",
- .patch = patch_vt3476},
+static const struct hda_device_id snd_hda_id_via[] = {
+ HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
+ HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1106*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
static struct hda_codec_driver via_driver = {
- .preset = snd_hda_preset_via,
+ .id = snd_hda_id_via,
};
MODULE_LICENSE("GPL");
diff --git a/kernel/sound/pci/ice1712/ice1712.c b/kernel/sound/pci/ice1712/ice1712.c
index f7b1523e8..8ae3bb797 100644
--- a/kernel/sound/pci/ice1712/ice1712.c
+++ b/kernel/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/kernel/sound/pci/ice1712/quartet.c b/kernel/sound/pci/ice1712/quartet.c
index 6f55e02e5..7c387b040 100644
--- a/kernel/sound/pci/ice1712/quartet.c
+++ b/kernel/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/kernel/sound/pci/intel8x0.c b/kernel/sound/pci/intel8x0.c
index b12092522..42bcbac80 100644
--- a/kernel/sound/pci/intel8x0.c
+++ b/kernel/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/kernel/sound/pci/intel8x0m.c b/kernel/sound/pci/intel8x0m.c
index 7577f31cd..1bc98c867 100644
--- a/kernel/sound/pci/intel8x0m.c
+++ b/kernel/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/kernel/sound/pci/korg1212/korg1212.c b/kernel/sound/pci/korg1212/korg1212.c
index 7acbc21d6..9e1ad119a 100644
--- a/kernel/sound/pci/korg1212/korg1212.c
+++ b/kernel/sound/pci/korg1212/korg1212.c
@@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
+
return 0;
}
@@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
return 0;
}
diff --git a/kernel/sound/pci/lx6464es/lx6464es.c b/kernel/sound/pci/lx6464es/lx6464es.c
index 601315a1f..8b8e2e54f 100644
--- a/kernel/sound/pci/lx6464es/lx6464es.c
+++ b/kernel/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, },
};
@@ -234,8 +234,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
/* the clock rate cannot be changed */
board_rate = chip->board_sample_rate;
- err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
- board_rate, board_rate);
+ err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ board_rate);
if (err < 0) {
dev_warn(chip->card->dev, "could not constrain periods\n");
@@ -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/kernel/sound/pci/maestro3.c b/kernel/sound/pci/maestro3.c
index 9be660993..17ae92613 100644
--- a/kernel/sound/pci/maestro3.c
+++ b/kernel/sound/pci/maestro3.c
@@ -1929,15 +1929,32 @@ snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
return;
snd_m3_outw(chip, val, CODEC_DATA);
snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+ /*
+ * Workaround for buggy ES1988 integrated AC'97 codec. It remains silent
+ * until the MASTER volume or mute is touched (alsactl restore does not
+ * work).
+ */
+ if (ac97->id == 0x45838308 && reg == AC97_MASTER) {
+ snd_m3_ac97_wait(chip);
+ snd_m3_outw(chip, val, CODEC_DATA);
+ snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+ }
}
-static void snd_m3_remote_codec_config(int io, int isremote)
+static void snd_m3_remote_codec_config(struct snd_m3 *chip, int isremote)
{
+ int io = chip->iobase;
+ u16 tmp;
+
isremote = isremote ? 1 : 0;
- outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
- io + RING_BUS_CTRL_B);
+ tmp = inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK;
+ /* enable dock on Dell Latitude C810 */
+ if (chip->pci->subsystem_vendor == 0x1028 &&
+ chip->pci->subsystem_device == 0x00e5)
+ tmp |= M3I_DOCK_ENABLE;
+ outw(tmp | isremote, io + RING_BUS_CTRL_B);
outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
io + SDO_OUT_DEST_CTRL);
outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
@@ -1989,7 +2006,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
if (!chip->irda_workaround)
dir |= 0x10; /* assuming pci bus master? */
- snd_m3_remote_codec_config(io, 0);
+ snd_m3_remote_codec_config(chip, 0);
outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
udelay(20);
@@ -2537,8 +2554,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/kernel/sound/pci/mixart/mixart.c b/kernel/sound/pci/mixart/mixart.c
index c3a9f39f8..bc81b9f75 100644
--- a/kernel/sound/pci/mixart/mixart.c
+++ b/kernel/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/kernel/sound/pci/oxygen/oxygen_lib.c b/kernel/sound/pci/oxygen/oxygen_lib.c
index ffff3b25f..b4ef58042 100644
--- a/kernel/sound/pci/oxygen/oxygen_lib.c
+++ b/kernel/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/kernel/sound/pci/oxygen/oxygen_mixer.c b/kernel/sound/pci/oxygen/oxygen_mixer.c
index 6492bca8c..4ca12665f 100644
--- a/kernel/sound/pci/oxygen/oxygen_mixer.c
+++ b/kernel/sound/pci/oxygen/oxygen_mixer.c
@@ -88,7 +88,7 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
int changed;
mutex_lock(&chip->mutex);
- changed = !value->value.integer.value[0] != chip->dac_mute;
+ changed = (!value->value.integer.value[0]) != chip->dac_mute;
if (changed) {
chip->dac_mute = !value->value.integer.value[0];
chip->model.update_dac_mute(chip);
diff --git a/kernel/sound/pci/oxygen/xonar_wm87x6.c b/kernel/sound/pci/oxygen/xonar_wm87x6.c
index 6ce68604c..90ac479f3 100644
--- a/kernel/sound/pci/oxygen/xonar_wm87x6.c
+++ b/kernel/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/kernel/sound/pci/pcxhr/pcxhr.c b/kernel/sound/pci/pcxhr/pcxhr.c
index c6092e48c..929323528 100644
--- a/kernel/sound/pci/pcxhr/pcxhr.c
+++ b/kernel/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/kernel/sound/pci/rme32.c b/kernel/sound/pci/rme32.c
index 23d7f5d30..cd94ac548 100644
--- a/kernel/sound/pci/rme32.c
+++ b/kernel/sound/pci/rme32.c
@@ -831,9 +831,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime)
{
if (! rme32->fullduplex_mode) {
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+ RME32_BUFFER_SIZE);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
&hw_constraints_period_bytes);
diff --git a/kernel/sound/pci/rme96.c b/kernel/sound/pci/rme96.c
index 2306ccf72..41c31db65 100644
--- a/kernel/sound/pci/rme96.c
+++ b/kernel/sound/pci/rme96.c
@@ -741,10 +741,11 @@ snd_rme96_playback_setrate(struct rme96 *rme96,
{
/* change to/from double-speed: reset the DAC (if available) */
snd_rme96_reset_dac(rme96);
+ return 1; /* need to restore volume */
} else {
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
}
- return 0;
}
static int
@@ -980,6 +981,7 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err, rate, dummy;
+ bool apply_dac_volume = false;
runtime->dma_area = (void __force *)(rme96->iobase +
RME96_IO_PLAY_BUFFER);
@@ -993,24 +995,26 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
{
/* slave clock */
if ((int)params_rate(params) != rate) {
- spin_unlock_irq(&rme96->lock);
- return -EIO;
- }
- } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
- return err;
- }
- if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
- return err;
+ err = -EIO;
+ goto error;
+ }
+ } else {
+ err = snd_rme96_playback_setrate(rme96, params_rate(params));
+ if (err < 0)
+ goto error;
+ apply_dac_volume = err > 0; /* need to restore volume later? */
}
+
+ err = snd_rme96_playback_setformat(rme96, params_format(params));
+ if (err < 0)
+ goto error;
snd_rme96_setframelog(rme96, params_channels(params), 1);
if (rme96->capture_periodsize != 0) {
if (params_period_size(params) << rme96->playback_frlog !=
rme96->capture_periodsize)
{
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
+ err = -EBUSY;
+ goto error;
}
}
rme96->playback_periodsize =
@@ -1021,9 +1025,16 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
+
+ err = 0;
+ error:
spin_unlock_irq(&rme96->lock);
-
- return 0;
+ if (apply_dac_volume) {
+ usleep_range(3000, 10000);
+ snd_rme96_apply_dac_volume(rme96);
+ }
+
+ return err;
}
static int
@@ -1152,13 +1163,13 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
{
unsigned int size;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ RME96_BUFFER_SIZE);
if ((size = rme96->playback_periodsize) != 0 ||
(size = rme96->capture_periodsize) != 0)
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- size, size);
+ size);
else
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
diff --git a/kernel/sound/pci/rme9652/hdsp.c b/kernel/sound/pci/rme9652/hdsp.c
index c19e021cc..7c8941b8b 100644
--- a/kernel/sound/pci/rme9652/hdsp.c
+++ b/kernel/sound/pci/rme9652/hdsp.c
@@ -1526,7 +1526,7 @@ static struct snd_rawmidi_ops snd_hdsp_midi_input =
static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int id)
{
- char buf[32];
+ char buf[40];
hdsp->midi[id].id = id;
hdsp->midi[id].rmidi = NULL;
@@ -1537,7 +1537,7 @@ static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int i
hdsp->midi[id].pending = 0;
spin_lock_init (&hdsp->midi[id].lock);
- sprintf (buf, "%s MIDI %d", card->shortname, id+1);
+ snprintf(buf, sizeof(buf), "%s MIDI %d", card->shortname, id + 1);
if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0)
return -1;
@@ -2806,7 +2806,8 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
offset = ucontrol->id.index - 1;
- snd_BUG_ON(offset < 0);
+ if (snd_BUG_ON(offset < 0))
+ return -EINVAL;
switch (hdsp->io_type) {
case Digiface:
@@ -2878,7 +2879,7 @@ static int snd_hdsp_get_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hdsp_dds_offset(hdsp);
+ ucontrol->value.integer.value[0] = hdsp_dds_offset(hdsp);
return 0;
}
@@ -2890,7 +2891,7 @@ static int snd_hdsp_put_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
- val = ucontrol->value.enumerated.item[0];
+ val = ucontrol->value.integer.value[0];
spin_lock_irq(&hdsp->lock);
if (val != hdsp_dds_offset(hdsp))
change = (hdsp_set_dds_offset(hdsp, val) == 0) ? 1 : 0;
@@ -5111,6 +5112,7 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
dev_err(hdsp->card->dev,
"too short firmware size %d (expected %d)\n",
(int)fw->size, HDSP_FIRMWARE_SIZE);
+ release_firmware(fw);
return -EINVAL;
}
diff --git a/kernel/sound/pci/rme9652/hdspm.c b/kernel/sound/pci/rme9652/hdspm.c
index cb666c737..a4a999a03 100644
--- a/kernel/sound/pci/rme9652/hdspm.c
+++ b/kernel/sound/pci/rme9652/hdspm.c
@@ -1601,6 +1601,9 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
{
u64 n;
+ if (snd_BUG_ON(rate <= 0))
+ return;
+
if (rate >= 112000)
rate /= 4;
else if (rate >= 56000)
@@ -2215,6 +2218,8 @@ static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
} else {
/* slave mode, return external sample rate */
rate = hdspm_external_sample_rate(hdspm);
+ if (!rate)
+ rate = hdspm->system_sample_rate;
}
}
@@ -2260,8 +2265,11 @@ static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol,
ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int rate = ucontrol->value.integer.value[0];
- hdspm_set_dds_value(hdspm, ucontrol->value.enumerated.item[0]);
+ if (rate < 27000 || rate > 207000)
+ return -EINVAL;
+ hdspm_set_dds_value(hdspm, ucontrol->value.integer.value[0]);
return 0;
}
@@ -4449,7 +4457,7 @@ static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hdspm->tco->term;
+ ucontrol->value.integer.value[0] = hdspm->tco->term;
return 0;
}
@@ -4460,8 +4468,8 @@ static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) {
- hdspm->tco->term = ucontrol->value.enumerated.item[0];
+ if (hdspm->tco->term != ucontrol->value.integer.value[0]) {
+ hdspm->tco->term = ucontrol->value.integer.value[0];
hdspm_tco_write(hdspm);
@@ -6080,18 +6088,17 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
32, 4096);
/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 16384, 16384);
+ 16384);
break;
default:
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
64, 8192);
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS,
- 2, 2);
+ snd_pcm_hw_constraint_single(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
break;
}
diff --git a/kernel/sound/pci/sis7019.c b/kernel/sound/pci/sis7019.c
index efe669b80..f3860b850 100644
--- a/kernel/sound/pci/sis7019.c
+++ b/kernel/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/kernel/sound/pci/sonicvibes.c b/kernel/sound/pci/sonicvibes.c
index 0f40624a4..1b6fad7d4 100644
--- a/kernel/sound/pci/sonicvibes.c
+++ b/kernel/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/kernel/sound/pci/trident/trident_main.c b/kernel/sound/pci/trident/trident_main.c
index b72be035f..599d2b7eb 100644
--- a/kernel/sound/pci/trident/trident_main.c
+++ b/kernel/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/kernel/sound/ppc/keywest.c b/kernel/sound/ppc/keywest.c
index 0d1c27e91..4373615f1 100644
--- a/kernel/sound/ppc/keywest.c
+++ b/kernel/sound/ppc/keywest.c
@@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "pmac.h"
@@ -31,10 +32,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 +58,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,15 +98,16 @@ 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 */
{ }
};
+MODULE_DEVICE_TABLE(i2c, keywest_i2c_id);
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 +139,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/kernel/sound/soc/Kconfig b/kernel/sound/soc/Kconfig
index 3ba52da18..7ff7d88e4 100644
--- a/kernel/sound/soc/Kconfig
+++ b/kernel/sound/soc/Kconfig
@@ -9,7 +9,6 @@ menuconfig SND_SOC
select SND_JACK if INPUT=y || INPUT=SND
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
- select SND_COMPRESS_OFFLOAD
---help---
If you want ASoC support, you should say Y here and also to the
@@ -30,6 +29,13 @@ config SND_SOC_GENERIC_DMAENGINE_PCM
bool
select SND_DMAENGINE_PCM
+config SND_SOC_COMPRESS
+ bool
+ select SND_COMPRESS_OFFLOAD
+
+config SND_SOC_TOPOLOGY
+ bool
+
# All the supported SoCs
source "sound/soc/adi/Kconfig"
source "sound/soc/atmel/Kconfig"
@@ -45,6 +51,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"
@@ -53,10 +60,13 @@ source "sound/soc/samsung/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
+source "sound/soc/sti/Kconfig"
+source "sound/soc/sunxi/Kconfig"
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/kernel/sound/soc/Makefile b/kernel/sound/soc/Makefile
index 974ba708b..8eb06db32 100644
--- a/kernel/sound/soc/Makefile
+++ b/kernel/sound/soc/Makefile
@@ -1,5 +1,10 @@
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-pcm.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
+
+ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
+snd-soc-core-objs += soc-topology.o
+endif
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
@@ -23,6 +28,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/
@@ -34,7 +40,10 @@ obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
+obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += sunxi/
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/kernel/sound/soc/atmel/Kconfig b/kernel/sound/soc/atmel/Kconfig
index e7d08806f..2d30464b8 100644
--- a/kernel/sound/soc/atmel/Kconfig
+++ b/kernel/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,19 @@ 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.
+
+config SND_ATMEL_SOC_CLASSD
+ tristate "Atmel ASoC driver for boards using CLASSD"
+ depends on ARCH_AT91 || COMPILE_TEST
+ select SND_ATMEL_SOC_DMA
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ CLASSD.
+endif
diff --git a/kernel/sound/soc/atmel/Makefile b/kernel/sound/soc/atmel/Makefile
index b327e5cc8..f6f7db428 100644
--- a/kernel/sound/soc/atmel/Makefile
+++ b/kernel/sound/soc/atmel/Makefile
@@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
snd-atmel-soc-wm8904-objs := atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
+snd-atmel-soc-classd-objs := atmel-classd.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
+obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
diff --git a/kernel/sound/soc/atmel/atmel-classd.c b/kernel/sound/soc/atmel/atmel-classd.c
new file mode 100644
index 000000000..827667573
--- /dev/null
+++ b/kernel/sound/soc/atmel/atmel-classd.c
@@ -0,0 +1,679 @@
+/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
+ *
+ * Copyright (C) 2015 Atmel
+ *
+ * Author: Songjun Wu <songjun.wu@atmel.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 or later
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "atmel-classd.h"
+
+struct atmel_classd_pdata {
+ bool non_overlap_enable;
+ int non_overlap_time;
+ int pwm_type;
+ const char *card_name;
+};
+
+struct atmel_classd {
+ dma_addr_t phy_base;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct clk *aclk;
+ int irq;
+ const struct atmel_classd_pdata *pdata;
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_classd_of_match[] = {
+ {
+ .compatible = "atmel,sama5d2-classd",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
+
+static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct atmel_classd_pdata *pdata;
+ const char *pwm_type;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "device node not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
+ if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
+ pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
+ else
+ pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
+
+ ret = of_property_read_u32(np,
+ "atmel,non-overlap-time", &pdata->non_overlap_time);
+ if (ret)
+ pdata->non_overlap_enable = false;
+ else
+ pdata->non_overlap_enable = true;
+
+ ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
+ if (ret)
+ pdata->card_name = "CLASSD";
+
+ return pdata;
+}
+#else
+static inline struct atmel_classd_pdata *
+atmel_classd_dt_init(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
+ | 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)
+
+static const struct snd_pcm_hardware atmel_classd_hw = {
+ .info = SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = ATMEL_CLASSD_RATES,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+};
+
+#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024)
+
+/* cpu dai component */
+static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ regmap_write(dd->regmap, CLASSD_THR, 0x0);
+
+ return clk_prepare_enable(dd->pclk);
+}
+
+static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->pclk);
+}
+
+static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
+ .startup = atmel_classd_cpu_dai_startup,
+ .shutdown = atmel_classd_cpu_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &atmel_classd_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
+ .name = "atmel-classd",
+};
+
+/* platform */
+static int
+atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ if (params_physical_width(params) != 16) {
+ dev_err(rtd->platform->dev,
+ "only supports 16-bit audio data\n");
+ return -EINVAL;
+ }
+
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr = dd->phy_base + CLASSD_THR;
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave_config->dst_maxburst = 1;
+ slave_config->src_maxburst = 1;
+ slave_config->device_fc = false;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_classd_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_classd_platform_configure_dma,
+ .pcm_hardware = &atmel_classd_hw,
+ .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+static const char * const mono_mode_text[] = {
+ "mix", "sat", "left", "right"
+};
+
+static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
+ mono_mode_text);
+
+static const char * const eqcfg_text[] = {
+ "Treble-12dB", "Treble-6dB",
+ "Medium-8dB", "Medium-3dB",
+ "Bass-12dB", "Bass-6dB",
+ "0 dB",
+ "Bass+6dB", "Bass+12dB",
+ "Medium+3dB", "Medium+8dB",
+ "Treble+6dB", "Treble+12dB",
+};
+
+static const unsigned int eqcfg_value[] = {
+ CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
+ CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
+ CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
+ CLASSD_INTPMR_EQCFG_FLAT,
+ CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
+ CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
+ CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
+ eqcfg_text, eqcfg_value);
+
+static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
+
+static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
+SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
+ CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
+ 78, 1, classd_digital_tlv),
+
+SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
+ CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
+
+SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
+
+SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
+
+SOC_ENUM("Mono Mode", classd_mono_mode_enum),
+
+SOC_ENUM("EQ", classd_eqcfg_enum),
+};
+
+static const char * const pwm_type[] = {
+ "Single ended", "Differential"
+};
+
+static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+ const struct atmel_classd_pdata *pdata = dd->pdata;
+ u32 mask, val;
+
+ mask = CLASSD_MR_PWMTYP_MASK;
+ val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
+
+ mask |= CLASSD_MR_NON_OVERLAP_MASK;
+ if (pdata->non_overlap_enable) {
+ val |= (CLASSD_MR_NON_OVERLAP_EN
+ << CLASSD_MR_NON_OVERLAP_SHIFT);
+
+ mask |= CLASSD_MR_NOVR_VAL_MASK;
+ switch (pdata->non_overlap_time) {
+ case 5:
+ val |= (CLASSD_MR_NOVR_VAL_5NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 10:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 15:
+ val |= (CLASSD_MR_NOVR_VAL_15NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 20:
+ val |= (CLASSD_MR_NOVR_VAL_20NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ default:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ dev_warn(codec->dev,
+ "non-overlapping value %d is invalid, the default value 10 is specified\n",
+ pdata->non_overlap_time);
+ break;
+ }
+ }
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ dev_info(codec->dev,
+ "PWM modulation type is %s, non-overlapping is %s\n",
+ pwm_type[pdata->pwm_type],
+ pdata->non_overlap_enable?"enabled":"disabled");
+
+ return 0;
+}
+
+static struct regmap *atmel_classd_codec_get_remap(struct device *dev)
+{
+ return dev_get_regmap(dev, NULL);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_classd = {
+ .probe = atmel_classd_codec_probe,
+ .controls = atmel_classd_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
+ .get_regmap = atmel_classd_codec_get_remap,
+};
+
+/* codec dai component */
+static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = clk_prepare_enable(dd->aclk);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
+ int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
+
+ if (mute)
+ val = mask;
+ else
+ val = 0;
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
+
+static struct {
+ int rate;
+ int sample_rate;
+ int dsp_clk;
+ unsigned long aclk_rate;
+} const sample_rates[] = {
+ { 8000, CLASSD_INTPMR_FRAME_8K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 16000, CLASSD_INTPMR_FRAME_16K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 32000, CLASSD_INTPMR_FRAME_32K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 48000, CLASSD_INTPMR_FRAME_48K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 96000, CLASSD_INTPMR_FRAME_96K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 22050, CLASSD_INTPMR_FRAME_22K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ { 44100, CLASSD_INTPMR_FRAME_44K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ { 88200, CLASSD_INTPMR_FRAME_88K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+};
+
+static int
+atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int fs;
+ int i, best, best_val, cur_val, ret;
+ u32 mask, val;
+
+ fs = params_rate(params);
+
+ best = 0;
+ best_val = abs(fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+
+ dev_dbg(codec->dev,
+ "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
+ sample_rates[best].rate, sample_rates[best].aclk_rate);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->aclk);
+
+ ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+ if (ret)
+ return ret;
+
+ mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
+ val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
+ | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
+
+ snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
+
+ ret = clk_prepare_enable(dd->aclk);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static void
+atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->aclk);
+}
+
+static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ snd_soc_update_bits(codec, CLASSD_MR,
+ CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
+ (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
+
+ return 0;
+}
+
+static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = {
+ .digital_mute = atmel_classd_codec_dai_digital_mute,
+ .startup = atmel_classd_codec_dai_startup,
+ .shutdown = atmel_classd_codec_dai_shutdown,
+ .hw_params = atmel_classd_codec_dai_hw_params,
+ .prepare = atmel_classd_codec_dai_prepare,
+ .trigger = atmel_classd_codec_dai_trigger,
+};
+
+#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
+
+static struct snd_soc_dai_driver atmel_classd_codec_dai = {
+ .name = ATMEL_CLASSD_CODEC_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &atmel_classd_codec_dai_ops,
+};
+
+/* ASoC sound card */
+static int atmel_classd_asoc_card_init(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_link)
+ return -ENOMEM;
+
+ dai_link->name = "CLASSD";
+ dai_link->stream_name = "CLASSD PCM";
+ dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME;
+ dai_link->cpu_dai_name = dev_name(dev);
+ dai_link->codec_name = dev_name(dev);
+ dai_link->platform_name = dev_name(dev);
+
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->name = dd->pdata->card_name;
+ card->dev = dev;
+
+ return 0;
+};
+
+/* regmap configuration */
+static const struct reg_default atmel_classd_reg_defaults[] = {
+ { CLASSD_INTPMR, 0x00301212 },
+};
+
+#define ATMEL_CLASSD_REG_MAX 0xE4
+static const struct regmap_config atmel_classd_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_CLASSD_REG_MAX,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = atmel_classd_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults),
+};
+
+static int atmel_classd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_classd *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ const struct atmel_classd_pdata *pdata;
+ struct snd_soc_card *card;
+ int ret;
+
+ pdata = dev_get_platdata(dev);
+ if (!pdata) {
+ pdata = atmel_classd_dt_init(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->pdata = pdata;
+
+ dd->irq = platform_get_irq(pdev, 0);
+ if (dd->irq < 0) {
+ ret = dd->irq;
+ dev_err(dev, "failed to could not get irq: %d\n", ret);
+ return ret;
+ }
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(dd->aclk)) {
+ ret = PTR_ERR(dd->aclk);
+ dev_err(dev, "failed to get audio clock: %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no memory resource\n");
+ return -ENXIO;
+ }
+
+ io_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(io_base)) {
+ ret = PTR_ERR(io_base);
+ dev_err(dev, "failed to remap register memory: %d\n", ret);
+ return ret;
+ }
+
+ dd->phy_base = res->start;
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &atmel_classd_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev,
+ &atmel_classd_cpu_dai_component,
+ &atmel_classd_cpu_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(dev,
+ &atmel_classd_dmaengine_pcm_config,
+ 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_classd,
+ &atmel_classd_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register codec: %d\n", ret);
+ return ret;
+ }
+
+ /* register sound card */
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, dd);
+ platform_set_drvdata(pdev, card);
+
+ ret = atmel_classd_asoc_card_init(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to init sound card\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to register sound card: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int atmel_classd_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver atmel_classd_driver = {
+ .driver = {
+ .name = "atmel-classd",
+ .of_match_table = of_match_ptr(atmel_classd_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_classd_probe,
+ .remove = atmel_classd_remove,
+};
+module_platform_driver(atmel_classd_driver);
+
+MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/atmel/atmel-classd.h b/kernel/sound/soc/atmel/atmel-classd.h
new file mode 100644
index 000000000..73f8fdd1c
--- /dev/null
+++ b/kernel/sound/soc/atmel/atmel-classd.h
@@ -0,0 +1,120 @@
+#ifndef __ATMEL_CLASSD_H_
+#define __ATMEL_CLASSD_H_
+
+#define CLASSD_CR 0x00000000
+#define CLASSD_CR_RESET 0x1
+
+#define CLASSD_MR 0x00000004
+
+#define CLASSD_MR_LEN_DIS 0x0
+#define CLASSD_MR_LEN_EN 0x1
+#define CLASSD_MR_LEN_MASK (0x1 << 0)
+#define CLASSD_MR_LEN_SHIFT (0)
+
+#define CLASSD_MR_LMUTE_DIS 0x0
+#define CLASSD_MR_LMUTE_EN 0x1
+#define CLASSD_MR_LMUTE_SHIFT (0x1)
+#define CLASSD_MR_LMUTE_MASK (0x1 << 1)
+
+#define CLASSD_MR_REN_DIS 0x0
+#define CLASSD_MR_REN_EN 0x1
+#define CLASSD_MR_REN_MASK (0x1 << 4)
+#define CLASSD_MR_REN_SHIFT (4)
+
+#define CLASSD_MR_RMUTE_DIS 0x0
+#define CLASSD_MR_RMUTE_EN 0x1
+#define CLASSD_MR_RMUTE_SHIFT (0x5)
+#define CLASSD_MR_RMUTE_MASK (0x1 << 5)
+
+#define CLASSD_MR_PWMTYP_SINGLE 0x0
+#define CLASSD_MR_PWMTYP_DIFF 0x1
+#define CLASSD_MR_PWMTYP_MASK (0x1 << 8)
+#define CLASSD_MR_PWMTYP_SHIFT (8)
+
+#define CLASSD_MR_NON_OVERLAP_DIS 0x0
+#define CLASSD_MR_NON_OVERLAP_EN 0x1
+#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16)
+#define CLASSD_MR_NON_OVERLAP_SHIFT (16)
+
+#define CLASSD_MR_NOVR_VAL_5NS 0x0
+#define CLASSD_MR_NOVR_VAL_10NS 0x1
+#define CLASSD_MR_NOVR_VAL_15NS 0x2
+#define CLASSD_MR_NOVR_VAL_20NS 0x3
+#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20)
+#define CLASSD_MR_NOVR_VAL_SHIFT (20)
+
+#define CLASSD_INTPMR 0x00000008
+
+#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0)
+#define CLASSD_INTPMR_ATTL_SHIFT (0)
+#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8)
+#define CLASSD_INTPMR_ATTR_SHIFT (8)
+
+#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0
+#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1
+#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16)
+#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16)
+
+#define CLASSD_INTPMR_DEEMP_DIS 0x0
+#define CLASSD_INTPMR_DEEMP_EN 0x1
+#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18)
+#define CLASSD_INTPMR_DEEMP_SHIFT (18)
+
+#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0
+#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1
+#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19)
+#define CLASSD_INTPMR_SWAP_SHIFT (19)
+
+#define CLASSD_INTPMR_FRAME_8K 0x0
+#define CLASSD_INTPMR_FRAME_16K 0x1
+#define CLASSD_INTPMR_FRAME_32K 0x2
+#define CLASSD_INTPMR_FRAME_48K 0x3
+#define CLASSD_INTPMR_FRAME_96K 0x4
+#define CLASSD_INTPMR_FRAME_22K 0x5
+#define CLASSD_INTPMR_FRAME_44K 0x6
+#define CLASSD_INTPMR_FRAME_88K 0x7
+#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20)
+#define CLASSD_INTPMR_FRAME_SHIFT (20)
+
+#define CLASSD_INTPMR_EQCFG_FLAT 0x0
+#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1
+#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2
+#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3
+#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4
+#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5
+#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6
+#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7
+#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8
+#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9
+#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa
+#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb
+#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc
+#define CLASSD_INTPMR_EQCFG_SHIFT (24)
+
+#define CLASSD_INTPMR_MONO_DIS 0x0
+#define CLASSD_INTPMR_MONO_EN 0x1
+#define CLASSD_INTPMR_MONO_MASK (0x1 << 28)
+#define CLASSD_INTPMR_MONO_SHIFT (28)
+
+#define CLASSD_INTPMR_MONO_MODE_MIX 0x0
+#define CLASSD_INTPMR_MONO_MODE_SAT 0x1
+#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2
+#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3
+#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29)
+#define CLASSD_INTPMR_MONO_MODE_SHIFT (29)
+
+#define CLASSD_INTSR 0x0000000c
+
+#define CLASSD_THR 0x00000010
+
+#define CLASSD_IER 0x00000014
+
+#define CLASSD_IDR 0x00000018
+
+#define CLASSD_IMR 0x0000001c
+
+#define CLASSD_ISR 0x00000020
+
+#define CLASSD_WPMR 0x000000e4
+
+#endif
diff --git a/kernel/sound/soc/atmel/atmel-pcm-dma.c b/kernel/sound/soc/atmel/atmel-pcm-dma.c
index b6625c8c4..dd57a9eac 100644
--- a/kernel/sound/soc/atmel/atmel-pcm-dma.c
+++ b/kernel/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/kernel/sound/soc/atmel/atmel_ssc_dai.c b/kernel/sound/soc/atmel/atmel_ssc_dai.c
index 841d05946..ba8def566 100644
--- a/kernel/sound/soc/atmel/atmel_ssc_dai.c
+++ b/kernel/sound/soc/atmel/atmel_ssc_dai.c
@@ -290,7 +290,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
int dir, dir_mask;
int ret;
- pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
+ pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n",
ssc_readl(ssc_p->ssc->regs, SR));
/* Enable PMC peripheral clock for this SSC */
diff --git a/kernel/sound/soc/atmel/atmel_wm8904.c b/kernel/sound/soc/atmel/atmel_wm8904.c
index aa354e1c6..1933bcd46 100644
--- a/kernel/sound/soc/atmel/atmel_wm8904.c
+++ b/kernel/sound/soc/atmel/atmel_wm8904.c
@@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
{ .compatible = "atmel,asoc-wm8904", },
{ }
};
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
#endif
static struct platform_driver atmel_asoc_wm8904_driver = {
diff --git a/kernel/sound/soc/atmel/sam9g20_wm8731.c b/kernel/sound/soc/atmel/sam9g20_wm8731.c
index 8de836165..d7469cdd9 100644
--- a/kernel/sound/soc/atmel/sam9g20_wm8731.c
+++ b/kernel/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/kernel/sound/soc/au1x/db1000.c b/kernel/sound/soc/au1x/db1000.c
index 452f404ab..e97c32798 100644
--- a/kernel/sound/soc/au1x/db1000.c
+++ b/kernel/sound/soc/au1x/db1000.c
@@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &db1000_ac97;
card->dev = &pdev->dev;
- return snd_soc_register_card(card);
-}
-
-static int db1000_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- snd_soc_unregister_card(card);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver db1000_audio_driver = {
@@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = db1000_audio_probe,
- .remove = db1000_audio_remove,
};
module_platform_driver(db1000_audio_driver);
diff --git a/kernel/sound/soc/au1x/db1200.c b/kernel/sound/soc/au1x/db1200.c
index c75995f27..5c73061d9 100644
--- a/kernel/sound/soc/au1x/db1200.c
+++ b/kernel/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,
@@ -129,6 +129,8 @@ static struct snd_soc_dai_link db1300_i2s_dai = {
.cpu_dai_name = "au1xpsc_i2s.2",
.platform_name = "au1xpsc-pcm.2",
.codec_name = "wm8731.0-001b",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ops = &db1200_i2s_wm8731_ops,
};
@@ -146,6 +148,8 @@ static struct snd_soc_dai_link db1550_i2s_dai = {
.cpu_dai_name = "au1xpsc_i2s.3",
.platform_name = "au1xpsc-pcm.3",
.codec_name = "wm8731.0-001b",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ops = &db1200_i2s_wm8731_ops,
};
@@ -174,14 +178,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
card = db1200_cards[pid->driver_data];
card->dev = &pdev->dev;
- return snd_soc_register_card(card);
-}
-
-static int db1200_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- snd_soc_unregister_card(card);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver db1200_audio_driver = {
@@ -191,7 +188,6 @@ static struct platform_driver db1200_audio_driver = {
},
.id_table = db1200_pids,
.probe = db1200_audio_probe,
- .remove = db1200_audio_remove,
};
module_platform_driver(db1200_audio_driver);
diff --git a/kernel/sound/soc/au1x/dbdma2.c b/kernel/sound/soc/au1x/dbdma2.c
index dd94fea72..5741c0aa6 100644
--- a/kernel/sound/soc/au1x/dbdma2.c
+++ b/kernel/sound/soc/au1x/dbdma2.c
@@ -344,14 +344,8 @@ static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, dmadata);
- return snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
-}
-
-static int au1xpsc_pcm_drvremove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
-
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev,
+ &au1xpsc_soc_platform);
}
static struct platform_driver au1xpsc_pcm_driver = {
@@ -359,7 +353,6 @@ static struct platform_driver au1xpsc_pcm_driver = {
.name = "au1xpsc-pcm",
},
.probe = au1xpsc_pcm_drvprobe,
- .remove = au1xpsc_pcm_drvremove,
};
module_platform_driver(au1xpsc_pcm_driver);
diff --git a/kernel/sound/soc/au1x/dma.c b/kernel/sound/soc/au1x/dma.c
index 24cc7f40d..fcf5a9add 100644
--- a/kernel/sound/soc/au1x/dma.c
+++ b/kernel/sound/soc/au1x/dma.c
@@ -312,14 +312,8 @@ static int alchemy_pcm_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- return snd_soc_register_platform(&pdev->dev, &alchemy_pcm_soc_platform);
-}
-
-static int alchemy_pcm_drvremove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
-
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev,
+ &alchemy_pcm_soc_platform);
}
static struct platform_driver alchemy_pcmdma_driver = {
@@ -327,7 +321,6 @@ static struct platform_driver alchemy_pcmdma_driver = {
.name = "alchemy-pcm-dma",
},
.probe = alchemy_pcm_drvprobe,
- .remove = alchemy_pcm_drvremove,
};
module_platform_driver(alchemy_pcmdma_driver);
diff --git a/kernel/sound/soc/au1x/psc-i2s.c b/kernel/sound/soc/au1x/psc-i2s.c
index e742ef668..0bf9d62b9 100644
--- a/kernel/sound/soc/au1x/psc-i2s.c
+++ b/kernel/sound/soc/au1x/psc-i2s.c
@@ -296,7 +296,6 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
struct resource *iores, *dmares;
unsigned long sel;
- int ret;
struct au1xpsc_audio_data *wd;
wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
@@ -305,19 +304,9 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
return -ENOMEM;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iores)
- return -ENODEV;
-
- ret = -EBUSY;
- if (!devm_request_mem_region(&pdev->dev, iores->start,
- resource_size(iores),
- pdev->name))
- return -EBUSY;
-
- wd->mmio = devm_ioremap(&pdev->dev, iores->start,
- resource_size(iores));
- if (!wd->mmio)
- return -EBUSY;
+ wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(wd->mmio))
+ return PTR_ERR(wd->mmio);
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmares)
diff --git a/kernel/sound/soc/bcm/bcm2835-i2s.c b/kernel/sound/soc/bcm/bcm2835-i2s.c
index 03fa1cbf8..8c435beb2 100644
--- a/kernel/sound/soc/bcm/bcm2835-i2s.c
+++ b/kernel/sound/soc/bcm/bcm2835-i2s.c
@@ -862,6 +862,8 @@ static const struct of_device_id bcm2835_i2s_of_match[] = {
{},
};
+MODULE_DEVICE_TABLE(of, bcm2835_i2s_of_match);
+
static struct platform_driver bcm2835_i2s_driver = {
.probe = bcm2835_i2s_probe,
.driver = {
diff --git a/kernel/sound/soc/blackfin/bf5xx-ac97-pcm.c b/kernel/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 238913e03..02ad2606f 100644
--- a/kernel/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/kernel/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -450,13 +450,8 @@ static struct snd_soc_platform_driver bf5xx_ac97_soc_platform = {
static int bf5xx_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &bf5xx_ac97_soc_platform);
-}
-
-static int bf5xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev,
+ &bf5xx_ac97_soc_platform);
}
static struct platform_driver bf5xx_pcm_driver = {
@@ -465,7 +460,6 @@ static struct platform_driver bf5xx_pcm_driver = {
},
.probe = bf5xx_soc_platform_probe,
- .remove = bf5xx_soc_platform_remove,
};
module_platform_driver(bf5xx_pcm_driver);
diff --git a/kernel/sound/soc/blackfin/bf5xx-ad1836.c b/kernel/sound/soc/blackfin/bf5xx-ad1836.c
index 5bf1501e5..864df2616 100644
--- a/kernel/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/kernel/sound/soc/blackfin/bf5xx-ad1836.c
@@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
-static int bf5xx_ad1836_driver_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver bf5xx_ad1836_driver = {
.driver = {
.name = "bfin-snd-ad1836",
.pm = &snd_soc_pm_ops,
},
.probe = bf5xx_ad1836_driver_probe,
- .remove = bf5xx_ad1836_driver_remove,
};
module_platform_driver(bf5xx_ad1836_driver);
diff --git a/kernel/sound/soc/blackfin/bf5xx-i2s-pcm.c b/kernel/sound/soc/blackfin/bf5xx-i2s-pcm.c
index d95477afc..6cba211da 100644
--- a/kernel/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/kernel/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -342,13 +342,8 @@ static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
static int bfin_i2s_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &bf5xx_i2s_soc_platform);
-}
-
-static int bfin_i2s_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev,
+ &bf5xx_i2s_soc_platform);
}
static struct platform_driver bfin_i2s_pcm_driver = {
@@ -357,7 +352,6 @@ static struct platform_driver bfin_i2s_pcm_driver = {
},
.probe = bfin_i2s_soc_platform_probe,
- .remove = bfin_i2s_soc_platform_remove,
};
module_platform_driver(bfin_i2s_pcm_driver);
diff --git a/kernel/sound/soc/blackfin/bfin-eval-adau1373.c b/kernel/sound/soc/blackfin/bfin-eval-adau1373.c
index 523baf582..72ac78988 100644
--- a/kernel/sound/soc/blackfin/bfin-eval-adau1373.c
+++ b/kernel/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adau1373);
-}
-
-static int bfin_eval_adau1373_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
}
static struct platform_driver bfin_eval_adau1373_driver = {
@@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1373_probe,
- .remove = bfin_eval_adau1373_remove,
};
module_platform_driver(bfin_eval_adau1373_driver);
diff --git a/kernel/sound/soc/blackfin/bfin-eval-adau1701.c b/kernel/sound/soc/blackfin/bfin-eval-adau1701.c
index f9e926dfd..5c67f72cf 100644
--- a/kernel/sound/soc/blackfin/bfin-eval-adau1701.c
+++ b/kernel/sound/soc/blackfin/bfin-eval-adau1701.c
@@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adau1701);
-}
-
-static int bfin_eval_adau1701_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
}
static struct platform_driver bfin_eval_adau1701_driver = {
@@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1701_probe,
- .remove = bfin_eval_adau1701_remove,
};
module_platform_driver(bfin_eval_adau1701_driver);
diff --git a/kernel/sound/soc/blackfin/bfin-eval-adau1x61.c b/kernel/sound/soc/blackfin/bfin-eval-adau1x61.c
index 4229f76da..fddfe00c9 100644
--- a/kernel/sound/soc/blackfin/bfin-eval-adau1x61.c
+++ b/kernel/sound/soc/blackfin/bfin-eval-adau1x61.c
@@ -108,6 +108,7 @@ static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
static struct snd_soc_card bfin_eval_adau1x61 = {
.name = "bfin-eval-adau1x61",
+ .owner = THIS_MODULE,
.driver_name = "eval-adau1x61",
.dai_link = &bfin_eval_adau1x61_dai,
.num_links = 1,
diff --git a/kernel/sound/soc/blackfin/bfin-eval-adav80x.c b/kernel/sound/soc/blackfin/bfin-eval-adav80x.c
index 27eee66af..1037477d1 100644
--- a/kernel/sound/soc/blackfin/bfin-eval-adav80x.c
+++ b/kernel/sound/soc/blackfin/bfin-eval-adav80x.c
@@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adav80x);
-}
-
-static int bfin_eval_adav80x_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
}
static const struct platform_device_id bfin_eval_adav80x_ids[] = {
@@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adav80x_probe,
- .remove = bfin_eval_adav80x_remove,
.id_table = bfin_eval_adav80x_ids,
};
diff --git a/kernel/sound/soc/cirrus/ep93xx-pcm.c b/kernel/sound/soc/cirrus/ep93xx-pcm.c
index 5f664471d..67a73330d 100644
--- a/kernel/sound/soc/cirrus/ep93xx-pcm.c
+++ b/kernel/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/kernel/sound/soc/codecs/88pm860x-codec.c b/kernel/sound/soc/codecs/88pm860x-codec.c
index a0f265327..e8bed6b0c 100644
--- a/kernel/sound/soc/codecs/88pm860x-codec.c
+++ b/kernel/sound/soc/codecs/88pm860x-codec.c
@@ -156,33 +156,29 @@ static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0);
/* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */
-static const unsigned int mic_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(mic_tlv,
0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0),
3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0),
- 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0),
-};
+ 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0)
+);
/* {0, 0, 0, -6, 0, 6, 12, 18}dB */
-static const unsigned int aux_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(aux_tlv,
0, 2, TLV_DB_SCALE_ITEM(0, 0, 0),
- 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0)
+);
/* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */
-static const unsigned int out_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(out_tlv,
0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1),
4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0),
5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0),
- 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0),
-};
+ 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0)
+);
-static const unsigned int st_tlv[] = {
- TLV_DB_RANGE_HEAD(8),
+static const DECLARE_TLV_DB_RANGE(st_tlv,
0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0),
2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0),
4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0),
@@ -190,8 +186,8 @@ static const unsigned int st_tlv[] = {
8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0),
10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0),
14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0),
- 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0),
-};
+ 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0)
+);
/* Sidetone Gain = M * 2^(-5-N) */
struct st_gain {
@@ -1028,10 +1024,8 @@ static int pm860x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
if (dir == PM860X_CLK_DIR_OUT)
pm860x->dir = PM860X_CLK_DIR_OUT;
- else {
- pm860x->dir = PM860X_CLK_DIR_IN;
+ else /* Slave mode is not supported */
return -EINVAL;
- }
return 0;
}
@@ -1140,7 +1134,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 +1150,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 +1180,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 +1201,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/kernel/sound/soc/codecs/Kconfig b/kernel/sound/soc/codecs/Kconfig
index 061c46587..cfdafc4c1 100644
--- a/kernel/sound/soc/codecs/Kconfig
+++ b/kernel/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
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4554
+ select SND_SOC_AK4613 if I2C
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
@@ -53,15 +54,19 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_I2C if I2C
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
+ select SND_SOC_CS4349 if I2C
select SND_SOC_CX20442 if TTY
- select SND_SOC_DA7210 if I2C
+ select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
+ select SND_SOC_DA7219 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_DMIC
select SND_SOC_BT_SCO
select SND_SOC_ES8328_SPI if SPI_MASTER
select SND_SOC_ES8328_I2C if I2C
+ select SND_SOC_GTM601
+ select SND_SOC_ICS43432
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@@ -76,13 +81,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
- select SND_SOC_HDMI_CODEC
+ select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
+ select SND_SOC_RT298 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
@@ -102,8 +108,10 @@ config SND_SOC_ALL_CODECS
select SND_SOC_STA350 if I2C
select SND_SOC_STA529 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
+ select SND_SOC_STI_SAS
select SND_SOC_TAS2552 if I2C
select SND_SOC_TAS5086 if I2C
+ select SND_SOC_TAS571X if I2C
select SND_SOC_TFA9879 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -165,6 +173,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8996 if I2C
select SND_SOC_WM8997 if MFD_WM8997
+ select SND_SOC_WM8998 if MFD_WM8998
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -189,9 +198,11 @@ config SND_SOC_ARIZONA
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM8997=y
+ default y if SND_SOC_WM8998=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM8997=m
+ default m if SND_SOC_WM8998=m
config SND_SOC_WM_HUBS
tristate
@@ -211,8 +222,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
@@ -312,6 +324,10 @@ config SND_SOC_AK4535
config SND_SOC_AK4554
tristate "AKM AK4554 CODEC"
+config SND_SOC_AK4613
+ tristate "AKM AK4613 CODEC"
+ depends on I2C
+
config SND_SOC_AK4641
tristate
@@ -401,6 +417,11 @@ config SND_SOC_CS42XX8_I2C
select SND_SOC_CS42XX8
select REGMAP_I2C
+# Cirrus Logic CS4349 HiFi DAC
+config SND_SOC_CS4349
+ tristate "Cirrus Logic CS4349 CODEC"
+ depends on I2C
+
config SND_SOC_CX20442
tristate
depends on TTY
@@ -418,6 +439,9 @@ config SND_SOC_DA7210
config SND_SOC_DA7213
tristate
+config SND_SOC_DA7219
+ tristate
+
config SND_SOC_DA732X
tristate
@@ -430,9 +454,6 @@ config SND_SOC_BT_SCO
config SND_SOC_DMIC
tristate
-config SND_SOC_HDMI_CODEC
- tristate "HDMI stub CODEC"
-
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
@@ -444,6 +465,12 @@ config SND_SOC_ES8328_SPI
tristate
select SND_SOC_ES8328
+config SND_SOC_GTM601
+ tristate 'GTM601 UMTS modem audio codec'
+
+config SND_SOC_ICS43432
+ tristate
+
config SND_SOC_ISABELLE
tristate
@@ -507,10 +534,21 @@ 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 y if SND_SOC_RT298=y
+ default m if SND_SOC_RT286=m
+ default m if SND_SOC_RT298=m
+
config SND_SOC_RT286
tristate
depends on I2C
+config SND_SOC_RT298
+ tristate
+ depends on I2C
+
config SND_SOC_RT5631
tristate "Realtek ALC5631/RT5631 CODEC"
depends on I2C
@@ -603,6 +641,9 @@ config SND_SOC_STA529
config SND_SOC_STAC9766
tristate
+config SND_SOC_STI_SAS
+ tristate "codec Audio support for STI SAS codec"
+
config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
@@ -611,6 +652,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
@@ -829,6 +874,9 @@ config SND_SOC_WM8996
config SND_SOC_WM8997
tristate
+config SND_SOC_WM8998
+ tristate
+
config SND_SOC_WM9081
tristate
@@ -860,6 +908,9 @@ config SND_SOC_MC13783
config SND_SOC_ML26124
tristate
+config SND_SOC_NAU8825
+ tristate
+
config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
diff --git a/kernel/sound/soc/codecs/Makefile b/kernel/sound/soc/codecs/Makefile
index abe2d7edf..f632fc42f 100644
--- a/kernel/sound/soc/codecs/Makefile
+++ b/kernel/sound/soc/codecs/Makefile
@@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4554-objs := ak4554.o
+snd-soc-ak4613-objs := ak4613.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
@@ -45,9 +46,11 @@ snd-soc-cs4271-i2c-objs := cs4271-i2c.o
snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
+snd-soc-cs4349-objs := cs4349.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
+snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-bt-sco-objs := bt-sco.o
@@ -55,6 +58,8 @@ snd-soc-dmic-objs := dmic.o
snd-soc-es8328-objs := es8328.o
snd-soc-es8328-i2c-objs := es8328-i2c.o
snd-soc-es8328-spi-objs := es8328-spi.o
+snd-soc-gtm601-objs := gtm601.o
+snd-soc-ics43432-objs := ics43432.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-l3-objs := l3.o
@@ -69,7 +74,7 @@ snd-soc-max98925-objs := max98925.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
-snd-soc-hdmi-codec-objs := hdmi.o
+snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
@@ -77,7 +82,9 @@ 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-rt298-objs := rt298.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
@@ -105,7 +112,9 @@ snd-soc-sta32x-objs := sta32x.o
snd-soc-sta350-objs := sta350.o
snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
+snd-soc-sti-sas-objs := sti-sas.o
snd-soc-tas5086-objs := tas5086.o
+snd-soc-tas571x-objs := tas571x.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -169,6 +178,7 @@ snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm8997-objs := wm8997.o
+snd-soc-wm8998-objs := wm8998.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
@@ -209,6 +219,7 @@ obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
+obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
@@ -230,9 +241,11 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
+obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
+obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
@@ -240,6 +253,8 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
+obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
@@ -254,7 +269,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
-obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
@@ -262,7 +277,9 @@ 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_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
@@ -286,8 +303,10 @@ obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
+obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
+obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
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
@@ -350,6 +369,7 @@ obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
+obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
diff --git a/kernel/sound/soc/codecs/ab8500-codec.c b/kernel/sound/soc/codecs/ab8500-codec.c
index 88ca9cb0c..affb19223 100644
--- a/kernel/sound/soc/codecs/ab8500-codec.c
+++ b/kernel/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);
@@ -1335,11 +1335,10 @@ static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1);
static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1);
/* -1dB = Mute */
-static const unsigned int hs_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(hs_gain_tlv,
0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0),
- 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0),
-};
+ 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0)
+);
static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
@@ -1947,6 +1946,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 +1973,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 +2461,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 +2542,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/kernel/sound/soc/codecs/ac97.c b/kernel/sound/soc/codecs/ac97.c
index d0ac723ee..5b3224c63 100644
--- a/kernel/sound/soc/codecs/ac97.c
+++ b/kernel/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/kernel/sound/soc/codecs/ad1836.c b/kernel/sound/soc/codecs/ad1836.c
index 685998dd0..e2ce6c4d7 100644
--- a/kernel/sound/soc/codecs/ad1836.c
+++ b/kernel/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;
@@ -404,7 +404,6 @@ MODULE_DEVICE_TABLE(spi, ad1836_ids);
static struct spi_driver ad1836_spi_driver = {
.driver = {
.name = "ad1836",
- .owner = THIS_MODULE,
},
.probe = ad1836_spi_probe,
.remove = ad1836_spi_remove,
diff --git a/kernel/sound/soc/codecs/ad193x-i2c.c b/kernel/sound/soc/codecs/ad193x-i2c.c
index df3a1a415..171313664 100644
--- a/kernel/sound/soc/codecs/ad193x-i2c.c
+++ b/kernel/sound/soc/codecs/ad193x-i2c.c
@@ -15,8 +15,8 @@
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
- { "ad1936", 0 },
- { "ad1937", 0 },
+ { "ad1936", AD193X },
+ { "ad1937", AD193X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
@@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client,
config.val_bits = 8;
config.reg_bits = 8;
- return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
+ return ad193x_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ (enum ad193x_type)id->driver_data);
}
static int ad193x_i2c_remove(struct i2c_client *client)
diff --git a/kernel/sound/soc/codecs/ad193x-spi.c b/kernel/sound/soc/codecs/ad193x-spi.c
index 390cef9b9..23c28573b 100644
--- a/kernel/sound/soc/codecs/ad193x-spi.c
+++ b/kernel/sound/soc/codecs/ad193x-spi.c
@@ -16,6 +16,7 @@
static int ad193x_spi_probe(struct spi_device *spi)
{
+ const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
config = ad193x_regmap_config;
@@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi)
config.read_flag_mask = 0x09;
config.write_flag_mask = 0x08;
- return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+ return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config),
+ (enum ad193x_type)id->driver_data);
}
static int ad193x_spi_remove(struct spi_device *spi)
@@ -33,13 +35,24 @@ static int ad193x_spi_remove(struct spi_device *spi)
return 0;
}
+static const struct spi_device_id ad193x_spi_id[] = {
+ { "ad193x", AD193X },
+ { "ad1933", AD1933 },
+ { "ad1934", AD1934 },
+ { "ad1938", AD193X },
+ { "ad1939", AD193X },
+ { "adau1328", AD193X },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad193x_spi_id);
+
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x",
- .owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
.remove = ad193x_spi_remove,
+ .id_table = ad193x_spi_id,
};
module_spi_driver(ad193x_spi_driver);
diff --git a/kernel/sound/soc/codecs/ad193x.c b/kernel/sound/soc/codecs/ad193x.c
index 17c953595..3a3f3f234 100644
--- a/kernel/sound/soc/codecs/ad193x.c
+++ b/kernel/sound/soc/codecs/ad193x.c
@@ -23,6 +23,7 @@
/* codec private data */
struct ad193x_priv {
struct regmap *regmap;
+ enum ad193x_type type;
int sysclk;
};
@@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
- AD193X_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
- AD193X_ADCR2_MUTE, 1, 1),
-
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
AD193X_DACR1_MUTE, 1, 1),
@@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
AD193X_DACR4_MUTE, 1, 1),
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
+};
+
+static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = {
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
+ AD193X_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
+ AD193X_ADCR2_MUTE, 1, 1),
+
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
AD193X_ADC_HIGHPASS_FILTER, 1, 0),
-
- /* DAC de-emphasis */
- SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
@@ -91,18 +97,33 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
{ "DAC Output", NULL, "DAC" },
{ "DAC Output", NULL, "VMID" },
- { "ADC", NULL, "SYSCLK" },
- { "DAC", NULL, "ADC_PWR" },
- { "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", NULL, "DAC Output" },
{ "DAC2OUT", NULL, "DAC Output" },
{ "DAC3OUT", NULL, "DAC Output" },
{ "DAC4OUT", NULL, "DAC Output" },
+ { "SYSCLK", NULL, "PLL_PWR" },
+};
+
+static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
+ { "ADC", NULL, "SYSCLK" },
+ { "ADC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
- { "SYSCLK", NULL, "PLL_PWR" },
};
+static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
+{
+ switch (ad193x->type) {
+ case AD1933:
+ case AD1934:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
/*
* DAI ops entries
*/
@@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_CHAN_MASK,
+ channels << AD193X_ADC_CHAN_SHFT);
return 0;
}
@@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
adc_serfmt |= AD193X_ADC_SERFMT_AUX;
break;
default:
- return -EINVAL;
+ if (ad193x_has_adc(ad193x))
+ return -EINVAL;
+ break;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_SERFMT_MASK, adc_serfmt);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_FMT_MASK, adc_fmt);
+ if (ad193x_has_adc(ad193x)) {
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_SERFMT_MASK, adc_serfmt);
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_FMT_MASK, adc_fmt);
+ }
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_FMT_MASK, dac_fmt);
@@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
AD193X_DAC_WORD_LEN_MASK,
word_len << AD193X_DAC_WORD_LEN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_WORD_LEN_MASK, word_len);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_WORD_LEN_MASK, word_len);
return 0;
}
@@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = {
static int ad193x_codec_probe(struct snd_soc_codec *codec)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int num, ret;
/* default setting for ad193x */
@@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
/* dac in tdm mode */
regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
- /* high-pass filter enable */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* high-pass filter enable */
+ regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
+ /* sata delay=1, adc aux mode */
+ regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+ }
+
/* pll input: mclki/xi */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* add adc controls */
+ num = ARRAY_SIZE(ad193x_adc_snd_controls);
+ ret = snd_soc_add_codec_controls(codec,
+ ad193x_adc_snd_controls,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc widgets */
+ num = ARRAY_SIZE(ad193x_adc_widgets);
+ ret = snd_soc_dapm_new_controls(dapm,
+ ad193x_adc_widgets,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc routes */
+ num = ARRAY_SIZE(ad193x_adc_audio_paths);
+ ret = snd_soc_dapm_add_routes(dapm,
+ ad193x_adc_audio_paths,
+ num);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -356,18 +418,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
.num_dapm_routes = ARRAY_SIZE(audio_paths),
};
-static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
const struct regmap_config ad193x_regmap_config = {
.max_register = AD193X_NUM_REGS - 1,
- .volatile_reg = adau193x_reg_volatile,
};
EXPORT_SYMBOL_GPL(ad193x_regmap_config);
-int ad193x_probe(struct device *dev, struct regmap *regmap)
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type)
{
struct ad193x_priv *ad193x;
@@ -379,6 +436,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
ad193x->regmap = regmap;
+ ad193x->type = type;
dev_set_drvdata(dev, ad193x);
diff --git a/kernel/sound/soc/codecs/ad193x.h b/kernel/sound/soc/codecs/ad193x.h
index ab9a998f1..8b1e65f92 100644
--- a/kernel/sound/soc/codecs/ad193x.h
+++ b/kernel/sound/soc/codecs/ad193x.h
@@ -13,8 +13,15 @@
struct device;
+enum ad193x_type {
+ AD193X,
+ AD1933,
+ AD1934,
+};
+
extern const struct regmap_config ad193x_regmap_config;
-int ad193x_probe(struct device *dev, struct regmap *regmap);
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type);
#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
diff --git a/kernel/sound/soc/codecs/ad1980.c b/kernel/sound/soc/codecs/ad1980.c
index 3cc69a626..9ef20dbcc 100644
--- a/kernel/sound/soc/codecs/ad1980.c
+++ b/kernel/sound/soc/codecs/ad1980.c
@@ -202,19 +202,21 @@ static struct snd_soc_dai_driver ad1980_dai = {
.formats = SND_SOC_STD_AC97_FMTS, },
};
+#define AD1980_VENDOR_ID 0x41445300
+#define AD1980_VENDOR_MASK 0xffffff00
+
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
{
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
unsigned int retry_cnt = 0;
+ int ret;
do {
- if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(ac97);
- if (snd_soc_read(codec, AC97_RESET) == 0x0090)
- return 1;
- }
+ ret = snd_ac97_reset(ac97, true, AD1980_VENDOR_ID,
+ AD1980_VENDOR_MASK);
+ if (ret >= 0)
+ return 0;
- soc_ac97_ops->reset(ac97);
/*
* Set bit 16slot in register 74h, then every slot will has only
* 16 bits. This command is sent out in 20bit mode, in which
@@ -223,8 +225,6 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
*/
snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
- if (snd_soc_read(codec, AC97_RESET) == 0x0090)
- return 0;
} while (retry_cnt++ < 10);
dev_err(codec->dev, "Failed to reset: AC97 link error\n");
@@ -240,7 +240,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
u16 vendor_id2;
u16 ext_status;
- ac97 = snd_soc_new_ac97_codec(codec);
+ ac97 = snd_soc_new_ac97_codec(codec, 0, 0);
if (IS_ERR(ac97)) {
ret = PTR_ERR(ac97);
dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
@@ -260,22 +260,10 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
if (ret < 0)
goto reset_err;
- /* Read out vendor ID to make sure it is ad1980 */
- if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
- ret = -ENODEV;
- goto reset_err;
- }
-
vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
-
- if (vendor_id2 != 0x5370) {
- if (vendor_id2 != 0x5374) {
- ret = -ENODEV;
- goto reset_err;
- } else {
- dev_warn(codec->dev,
- "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
- }
+ if (vendor_id2 == 0x5374) {
+ dev_warn(codec->dev,
+ "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
}
/* unmute captures and playbacks volume */
diff --git a/kernel/sound/soc/codecs/adau1373.c b/kernel/sound/soc/codecs/adau1373.c
index 783dcb570..fe1353a79 100644
--- a/kernel/sound/soc/codecs/adau1373.c
+++ b/kernel/sound/soc/codecs/adau1373.c
@@ -320,13 +320,12 @@ static const struct reg_default adau1373_reg_defaults[] = {
{ ADAU1373_DIGEN, 0x00 },
};
-static const unsigned int adau1373_out_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(adau1373_out_tlv,
0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
- 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
-};
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0)
+);
static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
@@ -381,12 +380,11 @@ static const char *adau1373_bass_hpf_cutoff_text[] = {
"158Hz", "232Hz", "347Hz", "520Hz",
};
-static const unsigned int adau1373_bass_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(adau1373_bass_tlv,
0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
- 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
-};
+ 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0)
+);
static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
@@ -414,11 +412,10 @@ static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
-static const unsigned int adau1373_3d_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(adau1373_3d_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
- 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120),
-};
+ 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120)
+);
static const char *adau1373_lr_mux_text[] = {
"Mute",
@@ -1444,7 +1441,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;
}
@@ -1535,7 +1531,6 @@ MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
static struct i2c_driver adau1373_i2c_driver = {
.driver = {
.name = "adau1373",
- .owner = THIS_MODULE,
},
.probe = adau1373_i2c_probe,
.remove = adau1373_i2c_remove,
diff --git a/kernel/sound/soc/codecs/adau1701.c b/kernel/sound/soc/codecs/adau1701.c
index d4e219b6b..de53c0d7b 100644
--- a/kernel/sound/soc/codecs/adau1701.c
+++ b/kernel/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;
}
@@ -821,7 +915,6 @@ MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
diff --git a/kernel/sound/soc/codecs/adau1761-i2c.c b/kernel/sound/soc/codecs/adau1761-i2c.c
index 862796dec..348ccb17d 100644
--- a/kernel/sound/soc/codecs/adau1761-i2c.c
+++ b/kernel/sound/soc/codecs/adau1761-i2c.c
@@ -47,7 +47,6 @@ MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
static struct i2c_driver adau1761_i2c_driver = {
.driver = {
.name = "adau1761",
- .owner = THIS_MODULE,
},
.probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
diff --git a/kernel/sound/soc/codecs/adau1761-spi.c b/kernel/sound/soc/codecs/adau1761-spi.c
index cce2f11f1..8bc1fbd25 100644
--- a/kernel/sound/soc/codecs/adau1761-spi.c
+++ b/kernel/sound/soc/codecs/adau1761-spi.c
@@ -64,7 +64,6 @@ MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
static struct spi_driver adau1761_spi_driver = {
.driver = {
.name = "adau1761",
- .owner = THIS_MODULE,
},
.probe = adau1761_spi_probe,
.remove = adau1761_spi_remove,
diff --git a/kernel/sound/soc/codecs/adau1761.c b/kernel/sound/soc/codecs/adau1761.c
index a1baeee16..2f12477e5 100644
--- a/kernel/sound/soc/codecs/adau1761.c
+++ b/kernel/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/kernel/sound/soc/codecs/adau1781-i2c.c b/kernel/sound/soc/codecs/adau1781-i2c.c
index 2ce4362cc..0e32bba92 100644
--- a/kernel/sound/soc/codecs/adau1781-i2c.c
+++ b/kernel/sound/soc/codecs/adau1781-i2c.c
@@ -45,7 +45,6 @@ MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
static struct i2c_driver adau1781_i2c_driver = {
.driver = {
.name = "adau1781",
- .owner = THIS_MODULE,
},
.probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
diff --git a/kernel/sound/soc/codecs/adau1781-spi.c b/kernel/sound/soc/codecs/adau1781-spi.c
index 194686716..33a73ff78 100644
--- a/kernel/sound/soc/codecs/adau1781-spi.c
+++ b/kernel/sound/soc/codecs/adau1781-spi.c
@@ -62,7 +62,6 @@ MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
static struct spi_driver adau1781_spi_driver = {
.driver = {
.name = "adau1781",
- .owner = THIS_MODULE,
},
.probe = adau1781_spi_probe,
.remove = adau1781_spi_remove,
diff --git a/kernel/sound/soc/codecs/adau1781.c b/kernel/sound/soc/codecs/adau1781.c
index 35581f43f..fde906855 100644
--- a/kernel/sound/soc/codecs/adau1781.c
+++ b/kernel/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/kernel/sound/soc/codecs/adau17x1.c b/kernel/sound/soc/codecs/adau17x1.c
index fa2e690e5..fcf05b254 100644
--- a/kernel/sound/soc/codecs/adau17x1.c
+++ b/kernel/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/kernel/sound/soc/codecs/adau1977-i2c.c b/kernel/sound/soc/codecs/adau1977-i2c.c
index 9700e8c83..21e7394a9 100644
--- a/kernel/sound/soc/codecs/adau1977-i2c.c
+++ b/kernel/sound/soc/codecs/adau1977-i2c.c
@@ -46,7 +46,6 @@ MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
static struct i2c_driver adau1977_i2c_driver = {
.driver = {
.name = "adau1977",
- .owner = THIS_MODULE,
},
.probe = adau1977_i2c_probe,
.remove = adau1977_i2c_remove,
diff --git a/kernel/sound/soc/codecs/adau1977-spi.c b/kernel/sound/soc/codecs/adau1977-spi.c
index b05cf5da3..0b46d88b4 100644
--- a/kernel/sound/soc/codecs/adau1977-spi.c
+++ b/kernel/sound/soc/codecs/adau1977-spi.c
@@ -63,7 +63,6 @@ MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
static struct spi_driver adau1977_spi_driver = {
.driver = {
.name = "adau1977",
- .owner = THIS_MODULE,
},
.probe = adau1977_spi_probe,
.remove = adau1977_spi_remove,
diff --git a/kernel/sound/soc/codecs/adau1977.c b/kernel/sound/soc/codecs/adau1977.c
index 7ad8e156e..9bdd15f40 100644
--- a/kernel/sound/soc/codecs/adau1977.c
+++ b/kernel/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/kernel/sound/soc/codecs/adav801.c b/kernel/sound/soc/codecs/adav801.c
index 790fce33a..055f1228c 100644
--- a/kernel/sound/soc/codecs/adav801.c
+++ b/kernel/sound/soc/codecs/adav801.c
@@ -39,7 +39,6 @@ static int adav80x_spi_remove(struct spi_device *spi)
static struct spi_driver adav80x_spi_driver = {
.driver = {
.name = "adav801",
- .owner = THIS_MODULE,
},
.probe = adav80x_spi_probe,
.remove = adav80x_spi_remove,
diff --git a/kernel/sound/soc/codecs/adav803.c b/kernel/sound/soc/codecs/adav803.c
index 66d9fce34..52881faed 100644
--- a/kernel/sound/soc/codecs/adav803.c
+++ b/kernel/sound/soc/codecs/adav803.c
@@ -36,7 +36,6 @@ static int adav803_remove(struct i2c_client *client)
static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
- .owner = THIS_MODULE,
},
.probe = adav803_probe,
.remove = adav803_remove,
diff --git a/kernel/sound/soc/codecs/adav80x.c b/kernel/sound/soc/codecs/adav80x.c
index 3a91a00fb..acff8d620 100644
--- a/kernel/sound/soc/codecs/adav80x.c
+++ b/kernel/sound/soc/codecs/adav80x.c
@@ -113,7 +113,7 @@
#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
-static struct reg_default adav80x_reg_defaults[] = {
+static const struct reg_default adav80x_reg_defaults[] = {
{ ADAV80X_PLAYBACK_CTRL, 0x01 },
{ ADAV80X_AUX_IN_CTRL, 0x01 },
{ ADAV80X_REC_CTRL, 0x02 },
@@ -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;
}
@@ -728,8 +728,8 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
return 0;
- return snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, adav80x->rate);
}
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
@@ -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/kernel/sound/soc/codecs/ak4104.c b/kernel/sound/soc/codecs/ak4104.c
index 1fd7f72b2..595d02d76 100644
--- a/kernel/sound/soc/codecs/ak4104.c
+++ b/kernel/sound/soc/codecs/ak4104.c
@@ -344,7 +344,6 @@ MODULE_DEVICE_TABLE(spi, ak4104_id_table);
static struct spi_driver ak4104_spi_driver = {
.driver = {
.name = "ak4104",
- .owner = THIS_MODULE,
.of_match_table = ak4104_of_match,
},
.id_table = ak4104_id_table,
diff --git a/kernel/sound/soc/codecs/ak4535.c b/kernel/sound/soc/codecs/ak4535.c
index 9130d916f..54428c643 100644
--- a/kernel/sound/soc/codecs/ak4535.c
+++ b/kernel/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;
}
@@ -445,7 +444,6 @@ MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
- .owner = THIS_MODULE,
},
.probe = ak4535_i2c_probe,
.remove = ak4535_i2c_remove,
diff --git a/kernel/sound/soc/codecs/ak4613.c b/kernel/sound/soc/codecs/ak4613.c
new file mode 100644
index 000000000..07a266460
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4613.c
@@ -0,0 +1,497 @@
+/*
+ * ak4613.c -- Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on ak4642.c by Kuninori Morimoto
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1 0x00 /* Power Management 1 */
+#define PW_MGMT2 0x01 /* Power Management 2 */
+#define PW_MGMT3 0x02 /* Power Management 3 */
+#define CTRL1 0x03 /* Control 1 */
+#define CTRL2 0x04 /* Control 2 */
+#define DEMP1 0x05 /* De-emphasis1 */
+#define DEMP2 0x06 /* De-emphasis2 */
+#define OFD 0x07 /* Overflow Detect */
+#define ZRD 0x08 /* Zero Detect */
+#define ICTRL 0x09 /* Input Control */
+#define OCTRL 0x0a /* Output Control */
+#define LOUT1 0x0b /* LOUT1 Volume Control */
+#define ROUT1 0x0c /* ROUT1 Volume Control */
+#define LOUT2 0x0d /* LOUT2 Volume Control */
+#define ROUT2 0x0e /* ROUT2 Volume Control */
+#define LOUT3 0x0f /* LOUT3 Volume Control */
+#define ROUT3 0x10 /* ROUT3 Volume Control */
+#define LOUT4 0x11 /* LOUT4 Volume Control */
+#define ROUT4 0x12 /* ROUT4 Volume Control */
+#define LOUT5 0x13 /* LOUT5 Volume Control */
+#define ROUT5 0x14 /* ROUT5 Volume Control */
+#define LOUT6 0x15 /* LOUT6 Volume Control */
+#define ROUT6 0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN BIT(0)
+#define PMDAC BIT(1)
+#define PMADC BIT(2)
+#define PMVR BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL 0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL 0x3f
+
+/* CTRL1 */
+#define DIF0 BIT(3)
+#define DIF1 BIT(4)
+#define DIF2 BIT(5)
+#define TDM0 BIT(6)
+#define TDM1 BIT(7)
+#define NO_FMT (0xff)
+#define FMT_MASK (0xf8)
+
+/* CTRL2 */
+#define DFS_NORMAL_SPEED (0 << 2)
+#define DFS_DOUBLE_SPEED (1 << 2)
+#define DFS_QUAD_SPEED (2 << 2)
+
+struct ak4613_priv {
+ struct mutex lock;
+
+ unsigned int fmt;
+ u8 fmt_ctrl;
+ int cnt;
+};
+
+struct ak4613_formats {
+ unsigned int width;
+ unsigned int fmt;
+};
+
+struct ak4613_interface {
+ struct ak4613_formats capture;
+ struct ak4613_formats playback;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+ 0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+ { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 },
+ { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 },
+ { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 },
+ { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 },
+ { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+ { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+static const struct ak4613_interface ak4613_iface[] = {
+ /* capture */ /* playback */
+ [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) },
+ [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) },
+ [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) },
+ [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) },
+ [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) },
+};
+
+static const struct regmap_config ak4613_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x16,
+ .reg_defaults = ak4613_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+ { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+ { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("LOUT4"),
+ SND_SOC_DAPM_OUTPUT("LOUT5"),
+ SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT4"),
+ SND_SOC_DAPM_OUTPUT("ROUT5"),
+ SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+ SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+ SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+ SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+ SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+ {"LOUT1", NULL, "DAC1"},
+ {"LOUT2", NULL, "DAC2"},
+ {"LOUT3", NULL, "DAC3"},
+ {"LOUT4", NULL, "DAC4"},
+ {"LOUT5", NULL, "DAC5"},
+ {"LOUT6", NULL, "DAC6"},
+
+ {"ROUT1", NULL, "DAC1"},
+ {"ROUT2", NULL, "DAC2"},
+ {"ROUT3", NULL, "DAC3"},
+ {"ROUT4", NULL, "DAC4"},
+ {"ROUT5", NULL, "DAC5"},
+ {"ROUT6", NULL, "DAC6"},
+
+ {"DAC1", NULL, "Playback"},
+ {"DAC2", NULL, "Playback"},
+ {"DAC3", NULL, "Playback"},
+ {"DAC4", NULL, "Playback"},
+ {"DAC5", NULL, "Playback"},
+ {"DAC6", NULL, "Playback"},
+
+ {"Capture", NULL, "ADC1"},
+ {"Capture", NULL, "ADC2"},
+
+ {"ADC1", NULL, "LIN1"},
+ {"ADC2", NULL, "LIN2"},
+
+ {"ADC1", NULL, "RIN1"},
+ {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct device *dev = codec->dev;
+
+ mutex_lock(&priv->lock);
+ priv->cnt--;
+ if (priv->cnt < 0) {
+ dev_err(dev, "unexpected counter error\n");
+ priv->cnt = 0;
+ }
+ if (!priv->cnt)
+ priv->fmt_ctrl = NO_FMT;
+ mutex_unlock(&priv->lock);
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (fmt) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ priv->fmt = fmt;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak4613_dai_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 ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ const struct ak4613_formats *fmts;
+ struct device *dev = codec->dev;
+ unsigned int width = params_width(params);
+ unsigned int fmt = priv->fmt;
+ unsigned int rate;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int i, ret;
+ u8 fmt_ctrl, ctrl2;
+
+ rate = params_rate(params);
+ switch (rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ ctrl2 = DFS_NORMAL_SPEED;
+ break;
+ case 88200:
+ case 96000:
+ ctrl2 = DFS_DOUBLE_SPEED;
+ break;
+ case 176400:
+ case 192000:
+ ctrl2 = DFS_QUAD_SPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME
+ *
+ * It doesn't support TDM at this point
+ */
+ fmt_ctrl = NO_FMT;
+ for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
+ fmts = (is_play) ? &ak4613_iface[i].playback :
+ &ak4613_iface[i].capture;
+
+ if (fmts->fmt != fmt)
+ continue;
+
+ if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ if (fmts->width != width)
+ continue;
+ } else {
+ if (fmts->width < width)
+ continue;
+ }
+
+ fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
+ break;
+ }
+
+ ret = -EINVAL;
+ if (fmt_ctrl == NO_FMT)
+ goto hw_params_end;
+
+ mutex_lock(&priv->lock);
+ if ((priv->fmt_ctrl == NO_FMT) ||
+ (priv->fmt_ctrl == fmt_ctrl)) {
+ priv->fmt_ctrl = fmt_ctrl;
+ priv->cnt++;
+ ret = 0;
+ }
+ mutex_unlock(&priv->lock);
+
+ if (ret < 0)
+ goto hw_params_end;
+
+ snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
+ snd_soc_write(codec, CTRL2, ctrl2);
+
+hw_params_end:
+ if (ret < 0)
+ dev_warn(dev, "unsupported data width/format combination\n");
+
+ return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 mgmt1 = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ mgmt1 |= RSTN;
+ /* fall through */
+ case SND_SOC_BIAS_PREPARE:
+ mgmt1 |= PMADC | PMDAC;
+ /* fall through */
+ case SND_SOC_BIAS_STANDBY:
+ mgmt1 |= PMVR;
+ /* fall through */
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_write(codec, PW_MGMT1, mgmt1);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+ .shutdown = ak4613_dai_shutdown,
+ .set_fmt = ak4613_dai_set_fmt,
+ .hw_params = ak4613_dai_hw_params,
+};
+
+#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_64000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+ .name = "ak4613-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .ops = &ak4613_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_mark_dirty(regmap);
+ return regcache_sync(regmap);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+ .resume = ak4613_resume,
+ .set_bias_level = ak4613_set_bias_level,
+ .controls = ak4613_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4613_snd_controls),
+ .dapm_widgets = ak4613_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
+ .dapm_routes = ak4613_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+};
+
+static int ak4613_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+ const struct regmap_config *regmap_cfg;
+ struct regmap *regmap;
+ struct ak4613_priv *priv;
+
+ regmap_cfg = NULL;
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(ak4613_of_match, dev);
+ if (of_id)
+ regmap_cfg = of_id->data;
+ } else {
+ regmap_cfg = (const struct regmap_config *)id->driver_data;
+ }
+
+ if (!regmap_cfg)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->fmt_ctrl = NO_FMT;
+ priv->cnt = 0;
+
+ mutex_init(&priv->lock);
+
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+ &ak4613_dai, 1);
+}
+
+static int ak4613_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+ .driver = {
+ .name = "ak4613-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = ak4613_of_match,
+ },
+ .probe = ak4613_i2c_probe,
+ .remove = ak4613_i2c_remove,
+ .id_table = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/ak4641.c b/kernel/sound/soc/codecs/ak4641.c
index 81b54a270..b14176f8d 100644
--- a/kernel/sound/soc/codecs/ak4641.c
+++ b/kernel/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;
}
@@ -610,7 +609,6 @@ MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
static struct i2c_driver ak4641_i2c_driver = {
.driver = {
.name = "ak4641",
- .owner = THIS_MODULE,
},
.probe = ak4641_i2c_probe,
.remove = ak4641_i2c_remove,
diff --git a/kernel/sound/soc/codecs/ak4642.c b/kernel/sound/soc/codecs/ak4642.c
index 13585e88f..cda27c228 100644
--- a/kernel/sound/soc/codecs/ak4642.c
+++ b/kernel/sound/soc/codecs/ak4642.c
@@ -23,6 +23,8 @@
* AK4648 is tested.
*/
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
@@ -64,12 +66,15 @@
#define FIL1_0 0x1c
#define FIL1_1 0x1d
#define FIL1_2 0x1e
-#define FIL1_3 0x1f
+#define FIL1_3 0x1f /* The maximum valid register for ak4642 */
#define PW_MGMT4 0x20
#define MD_CTL5 0x21
#define LO_MS 0x22
#define HP_MS 0x23
-#define SPK_MS 0x24
+#define SPK_MS 0x24 /* The maximum valid register for ak4643 */
+#define EQ_FBEQAB 0x25
+#define EQ_FBEQCD 0x26
+#define EQ_FBEQE 0x27 /* The maximum valid register for ak4648 */
/* PW_MGMT1*/
#define PMVCM (1 << 6) /* VCOM Power Management */
@@ -125,11 +130,8 @@
#define I2S (3 << 0)
/* MD_CTL2 */
-#define FS0 (1 << 0)
-#define FS1 (1 << 1)
-#define FS2 (1 << 2)
-#define FS3 (1 << 5)
-#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val) ((val & 0x3) << 6)
/* MD_CTL3 */
#define BST1 (1 << 3)
@@ -144,6 +146,7 @@ struct ak4642_drvdata {
struct ak4642_priv {
const struct ak4642_drvdata *drvdata;
+ struct clk *mcko;
};
/*
@@ -241,7 +244,7 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = {
/*
* ak4642 register cache
*/
-static const struct reg_default ak4642_reg[] = {
+static const struct reg_default ak4643_reg[] = {
{ 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
{ 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
@@ -254,6 +257,14 @@ static const struct reg_default ak4642_reg[] = {
{ 36, 0x00 },
};
+/* The default settings for 0x0 ~ 0x1f registers are the same for ak4642
+ and ak4643. So we reuse the ak4643 reg_default for ak4642.
+ The valid registers for ak4642 are 0x0 ~ 0x1f which is a subset of ak4643,
+ so define NUM_AK4642_REG_DEFAULTS for ak4642.
+*/
+#define ak4642_reg ak4643_reg
+#define NUM_AK4642_REG_DEFAULTS (FIL1_3 + 1)
+
static const struct reg_default ak4648_reg[] = {
{ 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
@@ -419,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+ u32 frequency)
+{
+ u32 fs_list[] = {
+ [0] = 8000,
+ [1] = 12000,
+ [2] = 16000,
+ [3] = 24000,
+ [4] = 7350,
+ [5] = 11025,
+ [6] = 14700,
+ [7] = 22050,
+ [10] = 32000,
+ [11] = 48000,
+ [14] = 29400,
+ [15] = 44100,
+ };
+ u32 ps_list[] = {
+ [0] = 256,
+ [1] = 128,
+ [2] = 64,
+ [3] = 32
+ };
+ int ps, fs;
+
+ for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+ for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+ if (frequency == ps_list[ps] * fs_list[fs]) {
+ snd_soc_write(codec, MD_CTL2,
+ PSs(ps) | FSs(fs));
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int ak4642_dai_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;
- u8 rate;
+ struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ u32 rate = clk_get_rate(priv->mcko);
- switch (params_rate(params)) {
- case 7350:
- rate = FS2;
- break;
- case 8000:
- rate = 0;
- break;
- case 11025:
- rate = FS2 | FS0;
- break;
- case 12000:
- rate = FS0;
- break;
- case 14700:
- rate = FS2 | FS1;
- break;
- case 16000:
- rate = FS1;
- break;
- case 22050:
- rate = FS2 | FS1 | FS0;
- break;
- case 24000:
- rate = FS1 | FS0;
- break;
- case 29400:
- rate = FS3 | FS2 | FS1;
- break;
- case 32000:
- rate = FS3 | FS1;
- break;
- case 44100:
- rate = FS3 | FS2 | FS1 | FS0;
- break;
- case 48000:
- rate = FS3 | FS1 | FS0;
- break;
- default:
- return -EINVAL;
- }
- snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+ if (!rate)
+ rate = params_rate(params) * 256;
- return 0;
+ return ak4642_set_mcko(codec, rate);
}
static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -482,7 +493,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;
}
@@ -522,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
return 0;
}
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+ struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (priv->mcko)
+ ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+ return 0;
+}
+
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+ .probe = ak4642_probe,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
@@ -536,15 +557,23 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
static const struct regmap_config ak4642_regmap = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = ARRAY_SIZE(ak4642_reg) + 1,
+ .max_register = FIL1_3,
.reg_defaults = ak4642_reg,
- .num_reg_defaults = ARRAY_SIZE(ak4642_reg),
+ .num_reg_defaults = NUM_AK4642_REG_DEFAULTS,
+};
+
+static const struct regmap_config ak4643_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SPK_MS,
+ .reg_defaults = ak4643_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4643_reg),
};
static const struct regmap_config ak4648_regmap = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = ARRAY_SIZE(ak4648_reg) + 1,
+ .max_register = EQ_FBEQE,
.reg_defaults = ak4648_reg,
.num_reg_defaults = ARRAY_SIZE(ak4648_reg),
};
@@ -554,7 +583,7 @@ static const struct ak4642_drvdata ak4642_drvdata = {
};
static const struct ak4642_drvdata ak4643_drvdata = {
- .regmap_config = &ak4642_regmap,
+ .regmap_config = &ak4643_regmap,
};
static const struct ak4642_drvdata ak4648_drvdata = {
@@ -562,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = {
.extended_frequencies = 1,
};
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk *clk;
+ const char *clk_name = np->name;
+ const char *parent_clk_name = NULL;
+ u32 rate;
+
+ if (of_property_read_u32(np, "clock-frequency", &rate))
+ return NULL;
+
+ if (of_property_read_bool(np, "clocks"))
+ parent_clk_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+ (parent_clk_name) ? 0 : CLK_IS_ROOT,
+ rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
static const struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct device_node *np = i2c->dev.of_node;
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
const struct ak4642_drvdata *drvdata = NULL;
struct regmap *regmap;
struct ak4642_priv *priv;
+ struct clk *mcko = NULL;
if (np) {
const struct of_device_id *of_id;
- of_id = of_match_device(ak4642_of_match, &i2c->dev);
+ mcko = ak4642_of_parse_mcko(dev);
+ if (IS_ERR(mcko))
+ mcko = NULL;
+
+ of_id = of_match_device(ak4642_of_match, dev);
if (of_id)
drvdata = of_id->data;
} else {
@@ -582,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
}
if (!drvdata) {
- dev_err(&i2c->dev, "Unknown device type\n");
+ dev_err(dev, "Unknown device type\n");
return -EINVAL;
}
- priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->drvdata = drvdata;
+ priv->mcko = mcko;
i2c_set_clientdata(i2c, priv);
@@ -598,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(&i2c->dev,
+ return snd_soc_register_codec(dev,
&soc_codec_dev_ak4642, &ak4642_dai, 1);
}
@@ -627,7 +692,6 @@ MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
static struct i2c_driver ak4642_i2c_driver = {
.driver = {
.name = "ak4642-codec",
- .owner = THIS_MODULE,
.of_match_table = ak4642_of_match,
},
.probe = ak4642_i2c_probe,
diff --git a/kernel/sound/soc/codecs/ak4671.c b/kernel/sound/soc/codecs/ak4671.c
index 2a58b1dcc..c73a9f691 100644
--- a/kernel/sound/soc/codecs/ak4671.c
+++ b/kernel/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;
}
@@ -664,7 +663,6 @@ MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671-codec",
- .owner = THIS_MODULE,
},
.probe = ak4671_i2c_probe,
.remove = ak4671_i2c_remove,
diff --git a/kernel/sound/soc/codecs/alc5623.c b/kernel/sound/soc/codecs/alc5623.c
index 0e3579968..d2e3a3ef7 100644
--- a/kernel/sound/soc/codecs/alc5623.c
+++ b/kernel/sound/soc/codecs/alc5623.c
@@ -82,12 +82,11 @@ static int amp_mixer_event(struct snd_soc_dapm_widget *w,
static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
-static const unsigned int boost_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(boost_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
- 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = {
@@ -826,7 +825,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 +892,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);
@@ -1086,7 +1084,6 @@ MODULE_DEVICE_TABLE(of, alc5623_of_match);
static struct i2c_driver alc5623_i2c_driver = {
.driver = {
.name = "alc562x-codec",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(alc5623_of_match),
},
.probe = alc5623_i2c_probe,
diff --git a/kernel/sound/soc/codecs/alc5632.c b/kernel/sound/soc/codecs/alc5632.c
index db3283abb..4d3ba33eb 100644
--- a/kernel/sound/soc/codecs/alc5632.c
+++ b/kernel/sound/soc/codecs/alc5632.c
@@ -35,7 +35,7 @@
/*
* ALC5632 register cache
*/
-static struct reg_default alc5632_reg_defaults[] = {
+static const struct reg_default alc5632_reg_defaults[] = {
{ 2, 0x8080 }, /* R2 - Speaker Output Volume */
{ 4, 0x8080 }, /* R4 - Headphone Output Volume */
{ 6, 0x8080 }, /* R6 - AUXOUT Volume */
@@ -146,11 +146,10 @@ static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
/* -16.5db min scale, 1.5db steps, no mute */
static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
-static const unsigned int boost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(boost_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
- 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
-};
+ 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0)
+);
/* 0db min scale, 6 db steps, no mute */
static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
/* 0db min scalem 0.75db steps, no mute */
@@ -1000,7 +999,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;
}
@@ -1184,7 +1182,6 @@ MODULE_DEVICE_TABLE(of, alc5632_of_match);
static struct i2c_driver alc5632_i2c_driver = {
.driver = {
.name = "alc5632",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(alc5632_of_match),
},
.probe = alc5632_i2c_probe,
diff --git a/kernel/sound/soc/codecs/arizona.c b/kernel/sound/soc/codecs/arizona.c
index ee91edcf3..93b400800 100644
--- a/kernel/sound/soc/codecs/arizona.c
+++ b/kernel/sound/soc/codecs/arizona.c
@@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
0x4f5, 0x0da);
}
break;
+ default:
+ break;
}
return 0;
@@ -208,11 +210,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 +223,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 +260,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 +277,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 +285,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;
@@ -314,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"Tone Generator 2",
"Haptics",
"AEC",
+ "AEC2",
"Mic Mute Mixer",
"Noise Generator",
"IN1L",
@@ -421,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x05,
0x06, /* Haptics */
0x08, /* AEC */
+ 0x09, /* AEC2 */
0x0c, /* Noise mixer */
0x0d, /* Comfort noise */
0x10, /* IN1L */
@@ -525,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values);
const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ "12kHz", "24kHz", "48kHz", "96kHz", "192kHz",
+ "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz",
+ "4kHz", "8kHz", "16kHz", "32kHz",
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_text);
+
+const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x10, 0x11, 0x12, 0x13,
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) {
+ if (arizona_sample_rate_val[i] == rate_val)
+ return arizona_sample_rate_text[i];
+ }
+
+ return "Illegal";
+}
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
+
const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
};
@@ -689,6 +719,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
ARIZONA_IN_VU, val);
}
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+{
+ unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+ unsigned int val = snd_soc_read(codec, reg);
+
+ return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
@@ -725,6 +764,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
if (reg == 0)
arizona_in_set_vu(codec, 0);
+ break;
+ default:
+ break;
}
return 0;
@@ -806,6 +848,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
break;
}
break;
+ default:
+ break;
}
return 0;
@@ -851,24 +895,146 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_hp_ev);
-static unsigned int arizona_sysclk_48k_rates[] = {
+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_opclk_ref_48k_rates[] = {
6144000,
12288000,
24576000,
49152000,
- 73728000,
- 98304000,
- 147456000,
};
-static unsigned int arizona_sysclk_44k1_rates[] = {
+static unsigned int arizona_opclk_ref_44k1_rates[] = {
5644800,
11289600,
22579200,
45158400,
- 67737600,
- 90316800,
- 135475200,
};
static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
@@ -893,11 +1059,11 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
if (refclk % 8000)
- rates = arizona_sysclk_44k1_rates;
+ rates = arizona_opclk_ref_44k1_rates;
else
- rates = arizona_sysclk_48k_rates;
+ rates = arizona_opclk_ref_48k_rates;
- for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
+ for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
rates[ref] <= refclk; ref++) {
div = 1;
while (rates[ref] / div >= freq && div < 32) {
@@ -1238,7 +1404,7 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
- struct reg_default dac_comp[] = {
+ struct reg_sequence dac_comp[] = {
{ 0x80, 0x3 },
{ ARIZONA_DAC_COMP_1, 0 },
{ ARIZONA_DAC_COMP_2, 0 },
@@ -1266,7 +1432,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 +1448,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) {
@@ -1354,12 +1537,12 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
bool reconfig;
unsigned int aif_tx_state, aif_rx_state;
- if (params_rate(params) % 8000)
+ if (params_rate(params) % 4000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
- wl = snd_pcm_format_width(params_format(params));
+ wl = params_width(params);
if (tdm_slots) {
arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
@@ -1474,6 +1657,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 +1688,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)
@@ -1722,6 +1906,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
if (fll->arizona->rev < 3 || sync)
return init_ratio;
break;
+ case WM8998:
+ case WM1814:
+ if (sync)
+ return init_ratio;
+ break;
default:
return init_ratio;
}
@@ -2131,6 +2320,109 @@ 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);
+
+static bool arizona_eq_filter_unstable(bool mode, __be16 _a, __be16 _b)
+{
+ s16 a = be16_to_cpu(_a);
+ s16 b = be16_to_cpu(_b);
+
+ if (!mode) {
+ return abs(a) >= 4096;
+ } else {
+ if (abs(b) >= 4096)
+ return true;
+
+ return (abs((a << 16) / (4096 - b)) >= 4096 << 4);
+ }
+}
+
+int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ unsigned int val;
+ __be16 *data;
+ int len;
+ int ret;
+
+ len = params->num_regs * regmap_get_val_bytes(arizona->regmap);
+
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ data[0] &= cpu_to_be16(ARIZONA_EQ1_B1_MODE);
+
+ if (arizona_eq_filter_unstable(!!data[0], data[1], data[2]) ||
+ arizona_eq_filter_unstable(true, data[4], data[5]) ||
+ arizona_eq_filter_unstable(true, data[8], data[9]) ||
+ arizona_eq_filter_unstable(true, data[12], data[13]) ||
+ arizona_eq_filter_unstable(false, data[16], data[17])) {
+ dev_err(arizona->dev, "Rejecting unstable EQ coefficients\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = regmap_read(arizona->regmap, params->base, &val);
+ if (ret != 0)
+ goto out;
+
+ val &= ~ARIZONA_EQ1_B1_MODE;
+ data[0] |= cpu_to_be16(val);
+
+ ret = regmap_raw_write(arizona->regmap, params->base, data, len);
+
+out:
+ kfree(data);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_eq_coeff_put);
+
+int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ __be16 *data = (__be16 *)ucontrol->value.bytes.data;
+ s16 val = be16_to_cpu(*data);
+
+ if (abs(val) >= 4096) {
+ dev_err(arizona->dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
+
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/arizona.h b/kernel/sound/soc/codecs/arizona.h
index 14e8485b5..fea8b8ae8 100644
--- a/kernel/sound/soc/codecs/arizona.h
+++ b/kernel/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,14 +87,23 @@ 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
+#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+#define ARIZONA_GAINMUX_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv)
+
#define ARIZONA_MIXER_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
@@ -107,8 +119,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 = \
@@ -187,9 +199,27 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \
ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
+#define ARIZONA_EQ_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = arizona_eq_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 20, .mask = ~ARIZONA_EQ1_B1_MODE }) }
+
+#define ARIZONA_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = arizona_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
#define ARIZONA_RATE_ENUM_SIZE 4
+#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
extern const struct soc_enum arizona_isrc_fsl[];
extern const struct soc_enum arizona_isrc_fsh[];
@@ -210,6 +240,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);
@@ -220,6 +252,11 @@ extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
+extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
@@ -244,6 +281,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,
@@ -260,4 +303,7 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
bool diff);
+extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
+extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
#endif
diff --git a/kernel/sound/soc/codecs/bt-sco.c b/kernel/sound/soc/codecs/bt-sco.c
index e7238b890..b084ad113 100644
--- a/kernel/sound/soc/codecs/bt-sco.c
+++ b/kernel/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/kernel/sound/soc/codecs/cq93vc.c b/kernel/sound/soc/codecs/cq93vc.c
index d6dedd4ea..1c895a530 100644
--- a/kernel/sound/soc/codecs/cq93vc.c
+++ b/kernel/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/kernel/sound/soc/codecs/cs35l32.c b/kernel/sound/soc/codecs/cs35l32.c
index 60598b230..44c30fe3e 100644
--- a/kernel/sound/soc/codecs/cs35l32.c
+++ b/kernel/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>
@@ -75,33 +74,8 @@ static const struct reg_default cs35l32_reg_defaults[] = {
static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS35L32_DEVID_AB:
- case CS35L32_DEVID_CD:
- case CS35L32_DEVID_E:
- case CS35L32_FAB_ID:
- case CS35L32_REV_ID:
- case CS35L32_PWRCTL1:
- case CS35L32_PWRCTL2:
- case CS35L32_CLK_CTL:
- case CS35L32_BATT_THRESHOLD:
- case CS35L32_VMON:
- case CS35L32_BST_CPCP_CTL:
- case CS35L32_IMON_SCALING:
- case CS35L32_AUDIO_LED_MNGR:
- case CS35L32_ADSP_CTL:
- case CS35L32_CLASSD_CTL:
- case CS35L32_PROTECT_CTL:
- case CS35L32_INT_MASK_1:
- case CS35L32_INT_MASK_2:
- case CS35L32_INT_MASK_3:
- case CS35L32_INT_STATUS_1:
- case CS35L32_INT_STATUS_2:
- case CS35L32_INT_STATUS_3:
- case CS35L32_LED_STATUS:
- case CS35L32_FLASH_MODE:
- case CS35L32_MOVIE_MODE:
- case CS35L32_FLASH_TIMER:
- case CS35L32_FLASH_INHIBIT:
+ case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR:
+ case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT:
return true;
default:
return false;
@@ -111,15 +85,8 @@ static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS35L32_DEVID_AB:
- case CS35L32_DEVID_CD:
- case CS35L32_DEVID_E:
- case CS35L32_FAB_ID:
- case CS35L32_REV_ID:
- case CS35L32_INT_STATUS_1:
- case CS35L32_INT_STATUS_2:
- case CS35L32_INT_STATUS_3:
- case CS35L32_LED_STATUS:
+ case CS35L32_DEVID_AB ... CS35L32_REV_ID:
+ case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
return true;
default:
return false;
@@ -129,10 +96,7 @@ static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS35L32_INT_STATUS_1:
- case CS35L32_INT_STATUS_2:
- case CS35L32_INT_STATUS_3:
- case CS35L32_LED_STATUS:
+ case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
return true;
default:
return false;
@@ -277,7 +241,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
};
/* Current and threshold powerup sequence Pg37 in datasheet */
-static const struct reg_default cs35l32_monitor_patch[] = {
+static const struct reg_sequence cs35l32_monitor_patch[] = {
{ 0x00, 0x99 },
{ 0x48, 0x17 },
@@ -442,8 +406,7 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
if (IS_ERR(cs35l32->reset_gpio))
return PTR_ERR(cs35l32->reset_gpio);
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
/* initialize codec */
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);
@@ -537,8 +500,7 @@ static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
snd_soc_unregister_codec(&i2c_client->dev);
/* Hold down reset */
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
return 0;
}
@@ -552,8 +514,7 @@ static int cs35l32_runtime_suspend(struct device *dev)
regcache_mark_dirty(cs35l32->regmap);
/* Hold down reset */
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
/* remove power */
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
@@ -576,8 +537,7 @@ static int cs35l32_runtime_resume(struct device *dev)
return ret;
}
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
regcache_cache_only(cs35l32->regmap, false);
regcache_sync(cs35l32->regmap);
@@ -608,7 +568,6 @@ MODULE_DEVICE_TABLE(i2c, cs35l32_id);
static struct i2c_driver cs35l32_i2c_driver = {
.driver = {
.name = "cs35l32",
- .owner = THIS_MODULE,
.pm = &cs35l32_runtime_pm,
.of_match_table = cs35l32_of_match,
},
diff --git a/kernel/sound/soc/codecs/cs35l32.h b/kernel/sound/soc/codecs/cs35l32.h
index 31ab804a2..1d6c2508c 100644
--- a/kernel/sound/soc/codecs/cs35l32.h
+++ b/kernel/sound/soc/codecs/cs35l32.h
@@ -80,7 +80,7 @@ struct cs35l32_platform_data {
#define CS35L32_GAIN_MGR_MASK 0x08
#define CS35L32_ADSP_SHARE_MASK 0x08
#define CS35L32_ADSP_DATACFG_MASK 0x30
-#define CS35L32_SDOUT_3ST 0x80
+#define CS35L32_SDOUT_3ST 0x08
#define CS35L32_BATT_REC_MASK 0x0E
#define CS35L32_BATT_THRESH_MASK 0x30
diff --git a/kernel/sound/soc/codecs/cs4265.c b/kernel/sound/soc/codecs/cs4265.c
index cac48ddf3..55db19ddc 100644
--- a/kernel/sound/soc/codecs/cs4265.c
+++ b/kernel/sound/soc/codecs/cs4265.c
@@ -60,23 +60,7 @@ static const struct reg_default cs4265_reg_defaults[] = {
static bool cs4265_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS4265_PWRCTL:
- case CS4265_DAC_CTL:
- case CS4265_ADC_CTL:
- case CS4265_MCLK_FREQ:
- case CS4265_SIG_SEL:
- case CS4265_CHB_PGA_CTL:
- case CS4265_CHA_PGA_CTL:
- case CS4265_ADC_CTL2:
- case CS4265_DAC_CHA_VOL:
- case CS4265_DAC_CHB_VOL:
- case CS4265_DAC_CTL2:
- case CS4265_SPDIF_CTL1:
- case CS4265_SPDIF_CTL2:
- case CS4265_INT_MASK:
- case CS4265_STATUS_MODE_MSB:
- case CS4265_STATUS_MODE_LSB:
- case CS4265_CHIP_ID:
+ case CS4265_CHIP_ID ... CS4265_SPDIF_CTL2:
return true;
default:
return false;
@@ -457,14 +441,14 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
case SND_SOC_DAIFMT_RIGHT_J:
if (params_width(params) == 16) {
snd_soc_update_bits(codec, CS4265_DAC_CTL,
- CS4265_DAC_CTL_DIF, (1 << 5));
+ CS4265_DAC_CTL_DIF, (2 << 4));
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
- CS4265_SPDIF_CTL2_DIF, (1 << 7));
+ CS4265_SPDIF_CTL2_DIF, (2 << 6));
} else {
snd_soc_update_bits(codec, CS4265_DAC_CTL,
- CS4265_DAC_CTL_DIF, (3 << 5));
+ CS4265_DAC_CTL_DIF, (3 << 4));
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
- CS4265_SPDIF_CTL2_DIF, (1 << 7));
+ CS4265_SPDIF_CTL2_DIF, (3 << 6));
}
break;
case SND_SOC_DAIFMT_LEFT_J:
@@ -473,7 +457,7 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, CS4265_ADC_CTL,
CS4265_ADC_DIF, 0);
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
- CS4265_SPDIF_CTL2_DIF, (1 << 6));
+ CS4265_SPDIF_CTL2_DIF, 0);
break;
default:
@@ -503,7 +487,6 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec,
CS4265_PWRCTL_PDN);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -659,7 +642,6 @@ MODULE_DEVICE_TABLE(i2c, cs4265_id);
static struct i2c_driver cs4265_i2c_driver = {
.driver = {
.name = "cs4265",
- .owner = THIS_MODULE,
.of_match_table = cs4265_of_match,
},
.id_table = cs4265_id,
diff --git a/kernel/sound/soc/codecs/cs4270.c b/kernel/sound/soc/codecs/cs4270.c
index e6d4ff9fd..e07807d96 100644
--- a/kernel/sound/soc/codecs/cs4270.c
+++ b/kernel/sound/soc/codecs/cs4270.c
@@ -751,7 +751,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
.name = "cs4270",
- .owner = THIS_MODULE,
.of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
diff --git a/kernel/sound/soc/codecs/cs4271-i2c.c b/kernel/sound/soc/codecs/cs4271-i2c.c
index b264da030..dcb3223d7 100644
--- a/kernel/sound/soc/codecs/cs4271-i2c.c
+++ b/kernel/sound/soc/codecs/cs4271-i2c.c
@@ -48,7 +48,6 @@ MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
static struct i2c_driver cs4271_i2c_driver = {
.driver = {
.name = "cs4271",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_i2c_probe,
diff --git a/kernel/sound/soc/codecs/cs4271-spi.c b/kernel/sound/soc/codecs/cs4271-spi.c
index acd49d86e..1ff5f5201 100644
--- a/kernel/sound/soc/codecs/cs4271-spi.c
+++ b/kernel/sound/soc/codecs/cs4271-spi.c
@@ -42,7 +42,6 @@ static int cs4271_spi_remove(struct spi_device *spi)
static struct spi_driver cs4271_spi_driver = {
.driver = {
.name = "cs4271",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_spi_probe,
diff --git a/kernel/sound/soc/codecs/cs42l51-i2c.c b/kernel/sound/soc/codecs/cs42l51-i2c.c
index c40428f25..9bad47847 100644
--- a/kernel/sound/soc/codecs/cs42l51-i2c.c
+++ b/kernel/sound/soc/codecs/cs42l51-i2c.c
@@ -45,7 +45,6 @@ static int cs42l51_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
- .owner = THIS_MODULE,
.of_match_table = cs42l51_of_match,
},
.probe = cs42l51_i2c_probe,
diff --git a/kernel/sound/soc/codecs/cs42l52.c b/kernel/sound/soc/codecs/cs42l52.c
index 1589e7a88..47b97fcef 100644
--- a/kernel/sound/soc/codecs/cs42l52.c
+++ b/kernel/sound/soc/codecs/cs42l52.c
@@ -110,58 +110,7 @@ static const struct reg_default cs42l52_reg_defaults[] = {
static bool cs42l52_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS42L52_CHIP:
- case CS42L52_PWRCTL1:
- case CS42L52_PWRCTL2:
- case CS42L52_PWRCTL3:
- case CS42L52_CLK_CTL:
- case CS42L52_IFACE_CTL1:
- case CS42L52_IFACE_CTL2:
- case CS42L52_ADC_PGA_A:
- case CS42L52_ADC_PGA_B:
- case CS42L52_ANALOG_HPF_CTL:
- case CS42L52_ADC_HPF_FREQ:
- case CS42L52_ADC_MISC_CTL:
- case CS42L52_PB_CTL1:
- case CS42L52_MISC_CTL:
- case CS42L52_PB_CTL2:
- case CS42L52_MICA_CTL:
- case CS42L52_MICB_CTL:
- case CS42L52_PGAA_CTL:
- case CS42L52_PGAB_CTL:
- case CS42L52_PASSTHRUA_VOL:
- case CS42L52_PASSTHRUB_VOL:
- case CS42L52_ADCA_VOL:
- case CS42L52_ADCB_VOL:
- case CS42L52_ADCA_MIXER_VOL:
- case CS42L52_ADCB_MIXER_VOL:
- case CS42L52_PCMA_MIXER_VOL:
- case CS42L52_PCMB_MIXER_VOL:
- case CS42L52_BEEP_FREQ:
- case CS42L52_BEEP_VOL:
- case CS42L52_BEEP_TONE_CTL:
- case CS42L52_TONE_CTL:
- case CS42L52_MASTERA_VOL:
- case CS42L52_MASTERB_VOL:
- case CS42L52_HPA_VOL:
- case CS42L52_HPB_VOL:
- case CS42L52_SPKA_VOL:
- case CS42L52_SPKB_VOL:
- case CS42L52_ADC_PCM_MIXER:
- case CS42L52_LIMITER_CTL1:
- case CS42L52_LIMITER_CTL2:
- case CS42L52_LIMITER_AT_RATE:
- case CS42L52_ALC_CTL:
- case CS42L52_ALC_RATE:
- case CS42L52_ALC_THRESHOLD:
- case CS42L52_NOISE_GATE_CTL:
- case CS42L52_CLK_STATUS:
- case CS42L52_BATT_COMPEN:
- case CS42L52_BATT_LEVEL:
- case CS42L52_SPK_STATUS:
- case CS42L52_TEM_CTL:
- case CS42L52_THE_FOLDBACK:
- case CS42L52_CHARGE_PUMP:
+ case CS42L52_CHIP ... CS42L52_CHARGE_PUMP:
return true;
default:
return false;
@@ -196,11 +145,10 @@ static DECLARE_TLV_DB_SCALE(mix_tlv, -50, 50, 0);
static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0);
-static const unsigned int limiter_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(limiter_tlv,
0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
- 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
static const char * const cs42l52_adca_text[] = {
"Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"};
@@ -897,7 +845,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 +856,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;
}
@@ -920,7 +867,7 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
-static struct snd_soc_dai_ops cs42l52_ops = {
+static const struct snd_soc_dai_ops cs42l52_ops = {
.hw_params = cs42l52_pcm_hw_params,
.digital_mute = cs42l52_digital_mute,
.set_fmt = cs42l52_set_fmt,
@@ -956,7 +903,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;
@@ -1119,7 +1066,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
};
/* Current and threshold powerup sequence Pg37 */
-static const struct reg_default cs42l52_threshold_patch[] = {
+static const struct reg_sequence cs42l52_threshold_patch[] = {
{ 0x00, 0x99 },
{ 0x3E, 0xBA },
@@ -1286,7 +1233,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l52_id);
static struct i2c_driver cs42l52_i2c_driver = {
.driver = {
.name = "cs42l52",
- .owner = THIS_MODULE,
.of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
diff --git a/kernel/sound/soc/codecs/cs42l56.c b/kernel/sound/soc/codecs/cs42l56.c
index cbc654fe4..7cd5f769b 100644
--- a/kernel/sound/soc/codecs/cs42l56.c
+++ b/kernel/sound/soc/codecs/cs42l56.c
@@ -115,52 +115,7 @@ static const struct reg_default cs42l56_reg_defaults[] = {
static bool cs42l56_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS42L56_CHIP_ID_1:
- case CS42L56_CHIP_ID_2:
- case CS42L56_PWRCTL_1:
- case CS42L56_PWRCTL_2:
- case CS42L56_CLKCTL_1:
- case CS42L56_CLKCTL_2:
- case CS42L56_SERIAL_FMT:
- case CS42L56_CLASSH_CTL:
- case CS42L56_MISC_CTL:
- case CS42L56_INT_STATUS:
- case CS42L56_PLAYBACK_CTL:
- case CS42L56_DSP_MUTE_CTL:
- case CS42L56_ADCA_MIX_VOLUME:
- case CS42L56_ADCB_MIX_VOLUME:
- case CS42L56_PCMA_MIX_VOLUME:
- case CS42L56_PCMB_MIX_VOLUME:
- case CS42L56_ANAINPUT_ADV_VOLUME:
- case CS42L56_DIGINPUT_ADV_VOLUME:
- case CS42L56_MASTER_A_VOLUME:
- case CS42L56_MASTER_B_VOLUME:
- case CS42L56_BEEP_FREQ_ONTIME:
- case CS42L56_BEEP_FREQ_OFFTIME:
- case CS42L56_BEEP_TONE_CFG:
- case CS42L56_TONE_CTL:
- case CS42L56_CHAN_MIX_SWAP:
- case CS42L56_AIN_REFCFG_ADC_MUX:
- case CS42L56_HPF_CTL:
- case CS42L56_MISC_ADC_CTL:
- case CS42L56_GAIN_BIAS_CTL:
- case CS42L56_PGAA_MUX_VOLUME:
- case CS42L56_PGAB_MUX_VOLUME:
- case CS42L56_ADCA_ATTENUATOR:
- case CS42L56_ADCB_ATTENUATOR:
- case CS42L56_ALC_EN_ATTACK_RATE:
- case CS42L56_ALC_RELEASE_RATE:
- case CS42L56_ALC_THRESHOLD:
- case CS42L56_NOISE_GATE_CTL:
- case CS42L56_ALC_LIM_SFT_ZC:
- case CS42L56_AMUTE_HPLO_MUX:
- case CS42L56_HPA_VOLUME:
- case CS42L56_HPB_VOLUME:
- case CS42L56_LOA_VOLUME:
- case CS42L56_LOB_VOLUME:
- case CS42L56_LIM_THRESHOLD_CTL:
- case CS42L56_LIM_CTL_RELEASE_RATE:
- case CS42L56_LIM_ATTACK_RATE:
+ case CS42L56_CHIP_ID_1 ... CS42L56_LIM_ATTACK_RATE:
return true;
default:
return false;
@@ -185,21 +140,18 @@ static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0);
static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
-static const unsigned int ngnb_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(ngnb_tlv,
0, 1, TLV_DB_SCALE_ITEM(-8200, 600, 0),
- 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0),
-};
-static const unsigned int ngb_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+ 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0)
+);
+static const DECLARE_TLV_DB_RANGE(ngb_tlv,
0, 2, TLV_DB_SCALE_ITEM(-6400, 600, 0),
- 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0),
-};
-static const unsigned int alc_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+ 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0)
+);
+static const DECLARE_TLV_DB_RANGE(alc_tlv,
0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
- 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
static const char * const beep_config_text[] = {
"Off", "Single", "Multiple", "Continuous"
@@ -953,7 +905,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 +930,6 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
cs42l56->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -990,7 +941,7 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops cs42l56_ops = {
+static const struct snd_soc_dai_ops cs42l56_ops = {
.hw_params = cs42l56_pcm_hw_params,
.digital_mute = cs42l56_digital_mute,
.set_fmt = cs42l56_set_dai_fmt,
@@ -1026,7 +977,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;
@@ -1409,7 +1360,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l56_id);
static struct i2c_driver cs42l56_i2c_driver = {
.driver = {
.name = "cs42l56",
- .owner = THIS_MODULE,
.of_match_table = cs42l56_of_match,
},
.id_table = cs42l56_id,
diff --git a/kernel/sound/soc/codecs/cs42l73.c b/kernel/sound/soc/codecs/cs42l73.c
index 8ecedba79..42a8fd4e1 100644
--- a/kernel/sound/soc/codecs/cs42l73.c
+++ b/kernel/sound/soc/codecs/cs42l73.c
@@ -153,111 +153,18 @@ static bool cs42l73_volatile_register(struct device *dev, unsigned int reg)
static bool cs42l73_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS42L73_DEVID_AB:
- case CS42L73_DEVID_CD:
- case CS42L73_DEVID_E:
- case CS42L73_REVID:
- case CS42L73_PWRCTL1:
- case CS42L73_PWRCTL2:
- case CS42L73_PWRCTL3:
- case CS42L73_CPFCHC:
- case CS42L73_OLMBMSDC:
- case CS42L73_DMMCC:
- case CS42L73_XSPC:
- case CS42L73_XSPMMCC:
- case CS42L73_ASPC:
- case CS42L73_ASPMMCC:
- case CS42L73_VSPC:
- case CS42L73_VSPMMCC:
- case CS42L73_VXSPFS:
- case CS42L73_MIOPC:
- case CS42L73_ADCIPC:
- case CS42L73_MICAPREPGAAVOL:
- case CS42L73_MICBPREPGABVOL:
- case CS42L73_IPADVOL:
- case CS42L73_IPBDVOL:
- case CS42L73_PBDC:
- case CS42L73_HLADVOL:
- case CS42L73_HLBDVOL:
- case CS42L73_SPKDVOL:
- case CS42L73_ESLDVOL:
- case CS42L73_HPAAVOL:
- case CS42L73_HPBAVOL:
- case CS42L73_LOAAVOL:
- case CS42L73_LOBAVOL:
- case CS42L73_STRINV:
- case CS42L73_XSPINV:
- case CS42L73_ASPINV:
- case CS42L73_VSPINV:
- case CS42L73_LIMARATEHL:
- case CS42L73_LIMRRATEHL:
- case CS42L73_LMAXHL:
- case CS42L73_LIMARATESPK:
- case CS42L73_LIMRRATESPK:
- case CS42L73_LMAXSPK:
- case CS42L73_LIMARATEESL:
- case CS42L73_LIMRRATEESL:
- case CS42L73_LMAXESL:
- case CS42L73_ALCARATE:
- case CS42L73_ALCRRATE:
- case CS42L73_ALCMINMAX:
- case CS42L73_NGCAB:
- case CS42L73_ALCNGMC:
- case CS42L73_MIXERCTL:
- case CS42L73_HLAIPAA:
- case CS42L73_HLBIPBA:
- case CS42L73_HLAXSPAA:
- case CS42L73_HLBXSPBA:
- case CS42L73_HLAASPAA:
- case CS42L73_HLBASPBA:
- case CS42L73_HLAVSPMA:
- case CS42L73_HLBVSPMA:
- case CS42L73_XSPAIPAA:
- case CS42L73_XSPBIPBA:
- case CS42L73_XSPAXSPAA:
- case CS42L73_XSPBXSPBA:
- case CS42L73_XSPAASPAA:
- case CS42L73_XSPAASPBA:
- case CS42L73_XSPAVSPMA:
- case CS42L73_XSPBVSPMA:
- case CS42L73_ASPAIPAA:
- case CS42L73_ASPBIPBA:
- case CS42L73_ASPAXSPAA:
- case CS42L73_ASPBXSPBA:
- case CS42L73_ASPAASPAA:
- case CS42L73_ASPBASPBA:
- case CS42L73_ASPAVSPMA:
- case CS42L73_ASPBVSPMA:
- case CS42L73_VSPAIPAA:
- case CS42L73_VSPBIPBA:
- case CS42L73_VSPAXSPAA:
- case CS42L73_VSPBXSPBA:
- case CS42L73_VSPAASPAA:
- case CS42L73_VSPBASPBA:
- case CS42L73_VSPAVSPMA:
- case CS42L73_VSPBVSPMA:
- case CS42L73_MMIXCTL:
- case CS42L73_SPKMIPMA:
- case CS42L73_SPKMXSPA:
- case CS42L73_SPKMASPA:
- case CS42L73_SPKMVSPMA:
- case CS42L73_ESLMIPMA:
- case CS42L73_ESLMXSPA:
- case CS42L73_ESLMASPA:
- case CS42L73_ESLMVSPMA:
- case CS42L73_IM1:
- case CS42L73_IM2:
+ case CS42L73_DEVID_AB ... CS42L73_DEVID_E:
+ case CS42L73_REVID ... CS42L73_IM2:
return true;
default:
return false;
}
}
-static const unsigned int hpaloa_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(hpaloa_tlv,
0, 13, TLV_DB_SCALE_ITEM(-7600, 200, 0),
- 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0),
-};
+ 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0)
+);
static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2500, 0);
@@ -267,11 +174,10 @@ static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0);
-static const unsigned int limiter_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(limiter_tlv,
0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
- 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1);
@@ -1208,7 +1114,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 +1134,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;
}
@@ -1237,8 +1142,8 @@ static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate)
struct snd_soc_codec *codec = dai->codec;
int id = dai->id;
- return snd_soc_update_bits(codec, CS42L73_SPC(id),
- 0x7F, tristate << 7);
+ return snd_soc_update_bits(codec, CS42L73_SPC(id), CS42L73_SP_3ST,
+ tristate << 7);
}
static const struct snd_pcm_hw_constraint_list constraints_12_24 = {
@@ -1492,7 +1397,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l73_id);
static struct i2c_driver cs42l73_i2c_driver = {
.driver = {
.name = "cs42l73",
- .owner = THIS_MODULE,
.of_match_table = cs42l73_of_match,
},
.id_table = cs42l73_id,
diff --git a/kernel/sound/soc/codecs/cs42xx8-i2c.c b/kernel/sound/soc/codecs/cs42xx8-i2c.c
index 657dce27e..800c1d549 100644
--- a/kernel/sound/soc/codecs/cs42xx8-i2c.c
+++ b/kernel/sound/soc/codecs/cs42xx8-i2c.c
@@ -20,7 +20,7 @@
static int cs42xx8_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- u32 ret = cs42xx8_probe(&i2c->dev,
+ int ret = cs42xx8_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
if (ret)
return ret;
@@ -49,8 +49,8 @@ MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
static struct i2c_driver cs42xx8_i2c_driver = {
.driver = {
.name = "cs42xx8",
- .owner = THIS_MODULE,
.pm = &cs42xx8_pm,
+ .of_match_table = cs42xx8_of_match,
},
.probe = cs42xx8_i2c_probe,
.remove = cs42xx8_i2c_remove,
diff --git a/kernel/sound/soc/codecs/cs42xx8.c b/kernel/sound/soc/codecs/cs42xx8.c
index 670ebfe12..d562e1b9a 100644
--- a/kernel/sound/soc/codecs/cs42xx8.c
+++ b/kernel/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:
@@ -425,7 +425,7 @@ const struct cs42xx8_driver_data cs42888_data = {
};
EXPORT_SYMBOL_GPL(cs42888_data);
-static const struct of_device_id cs42xx8_of_match[] = {
+const struct of_device_id cs42xx8_of_match[] = {
{ .compatible = "cirrus,cs42448", .data = &cs42448_data, },
{ .compatible = "cirrus,cs42888", .data = &cs42888_data, },
{ /* sentinel */ }
@@ -435,16 +435,24 @@ EXPORT_SYMBOL_GPL(cs42xx8_of_match);
int cs42xx8_probe(struct device *dev, struct regmap *regmap)
{
- const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev);
+ const struct of_device_id *of_id;
struct cs42xx8_priv *cs42xx8;
int ret, val, i;
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL);
if (cs42xx8 == NULL)
return -ENOMEM;
+ cs42xx8->regmap = regmap;
dev_set_drvdata(dev, cs42xx8);
+ of_id = of_match_device(cs42xx8_of_match, dev);
if (of_id)
cs42xx8->drvdata = of_id->data;
@@ -482,13 +490,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
/* Make sure hardware reset done */
msleep(5);
- cs42xx8->regmap = regmap;
- if (IS_ERR(cs42xx8->regmap)) {
- ret = PTR_ERR(cs42xx8->regmap);
- dev_err(dev, "failed to allocate regmap: %d\n", ret);
- goto err_enable;
- }
-
/*
* We haven't marked the chip revision as volatile due to
* sharing a register with the right input volume; explicitly
diff --git a/kernel/sound/soc/codecs/cs42xx8.h b/kernel/sound/soc/codecs/cs42xx8.h
index b2c10e537..d36c61b6d 100644
--- a/kernel/sound/soc/codecs/cs42xx8.h
+++ b/kernel/sound/soc/codecs/cs42xx8.h
@@ -22,6 +22,7 @@ extern const struct dev_pm_ops cs42xx8_pm;
extern const struct cs42xx8_driver_data cs42448_data;
extern const struct cs42xx8_driver_data cs42888_data;
extern const struct regmap_config cs42xx8_regmap_config;
+extern const struct of_device_id cs42xx8_of_match[];
int cs42xx8_probe(struct device *dev, struct regmap *regmap);
/* CS42888 register map */
diff --git a/kernel/sound/soc/codecs/cs4349.c b/kernel/sound/soc/codecs/cs4349.c
new file mode 100644
index 000000000..0ac8fc5ed
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4349.c
@@ -0,0 +1,392 @@
+/*
+ * cs4349.c -- CS4349 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Tim Howe <Tim.Howe@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "cs4349.h"
+
+
+static const struct reg_default cs4349_reg_defaults[] = {
+ { 2, 0x00 }, /* r02 - Mode Control */
+ { 3, 0x09 }, /* r03 - Volume, Mixing and Inversion Control */
+ { 4, 0x81 }, /* r04 - Mute Control */
+ { 5, 0x00 }, /* r05 - Channel A Volume Control */
+ { 6, 0x00 }, /* r06 - Channel B Volume Control */
+ { 7, 0xB1 }, /* r07 - Ramp and Filter Control */
+ { 8, 0x1C }, /* r08 - Misc. Control */
+};
+
+/* Private data for the CS4349 */
+struct cs4349_private {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int mode;
+ int rate;
+};
+
+static bool cs4349_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4349_CHIPID ... CS4349_MISC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4349_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4349_MODE ... CS4349_MISC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec);
+ unsigned int fmt;
+
+ fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (fmt) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cs4349->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec);
+ int fmt, ret;
+
+ cs4349->rate = params_rate(params);
+
+ switch (cs4349->mode) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt = DIF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = DIF_LEFT_JST;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ fmt = DIF_RGHT_JST16;
+ break;
+ case 24:
+ fmt = DIF_RGHT_JST24;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_update_bits(codec, CS4349_MODE, DIF_MASK,
+ MODE_FORMAT(fmt));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs4349_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg;
+
+ reg = 0;
+ if (mute)
+ reg = MUTE_AB_MASK;
+
+ return snd_soc_update_bits(codec, CS4349_MUTE, MUTE_AB_MASK, reg);
+}
+
+static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0);
+
+static const char * const chan_mix_texts[] = {
+ "Mute", "MuteA", "MuteA SwapB", "MuteA MonoB", "SwapA MuteB",
+ "BothR", "Swap", "SwapA MonoB", "MuteB", "Normal", "BothL",
+ "MonoB", "MonoA MuteB", "MonoA", "MonoA SwapB", "Mono",
+ /*Normal == Channel A = Left, Channel B = Right*/
+};
+
+static const char * const fm_texts[] = {
+ "Auto", "Single", "Double", "Quad",
+};
+
+static const char * const deemph_texts[] = {
+ "None", "44.1k", "48k", "32k",
+};
+
+static const char * const softr_zeroc_texts[] = {
+ "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",
+};
+
+static int deemph_values[] = {
+ 0, 4, 8, 12,
+};
+
+static int softr_zeroc_values[] = {
+ 0, 64, 128, 192,
+};
+
+static const struct soc_enum chan_mix_enum =
+ SOC_ENUM_SINGLE(CS4349_VMI, 0,
+ ARRAY_SIZE(chan_mix_texts),
+ chan_mix_texts);
+
+static const struct soc_enum fm_mode_enum =
+ SOC_ENUM_SINGLE(CS4349_MODE, 0,
+ ARRAY_SIZE(fm_texts),
+ fm_texts);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(deemph_enum, CS4349_MODE, 0, DEM_MASK,
+ deemph_texts, deemph_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(softr_zeroc_enum, CS4349_RMPFLT, 0,
+ SR_ZC_MASK, softr_zeroc_texts,
+ softr_zeroc_values);
+
+static const struct snd_kcontrol_new cs4349_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS4349_VOLA, CS4349_VOLB, 0, 0xFF, 1, dig_tlv),
+ SOC_ENUM("Functional Mode", fm_mode_enum),
+ SOC_ENUM("De-Emphasis Control", deemph_enum),
+ SOC_ENUM("Soft Ramp Zero Cross Control", softr_zeroc_enum),
+ SOC_ENUM("Channel Mixer", chan_mix_enum),
+ SOC_SINGLE("VolA = VolB Switch", CS4349_VMI, 7, 1, 0),
+ SOC_SINGLE("InvertA Switch", CS4349_VMI, 6, 1, 0),
+ SOC_SINGLE("InvertB Switch", CS4349_VMI, 5, 1, 0),
+ SOC_SINGLE("Auto-Mute Switch", CS4349_MUTE, 7, 1, 0),
+ SOC_SINGLE("MUTEC A = B Switch", CS4349_MUTE, 5, 1, 0),
+ SOC_SINGLE("Soft Ramp Up Switch", CS4349_RMPFLT, 5, 1, 0),
+ SOC_SINGLE("Soft Ramp Down Switch", CS4349_RMPFLT, 4, 1, 0),
+ SOC_SINGLE("Slow Roll Off Filter Switch", CS4349_RMPFLT, 2, 1, 0),
+ SOC_SINGLE("Freeze Switch", CS4349_MISC, 5, 1, 0),
+ SOC_SINGLE("Popguard Switch", CS4349_MISC, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget cs4349_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OutputA"),
+ SND_SOC_DAPM_OUTPUT("OutputB"),
+};
+
+static const struct snd_soc_dapm_route cs4349_routes[] = {
+ {"DAC Playback", NULL, "OutputA"},
+ {"DAC Playback", NULL, "OutputB"},
+
+ {"OutputA", NULL, "HiFi DAC"},
+ {"OutputB", NULL, "HiFi DAC"},
+};
+
+#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000
+
+static const struct snd_soc_dai_ops cs4349_dai_ops = {
+ .hw_params = cs4349_pcm_hw_params,
+ .set_fmt = cs4349_set_dai_fmt,
+ .digital_mute = cs4349_digital_mute,
+};
+
+static struct snd_soc_dai_driver cs4349_dai = {
+ .name = "cs4349_hifi",
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4349_PCM_RATES,
+ .formats = CS4349_PCM_FORMATS,
+ },
+ .ops = &cs4349_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs4349 = {
+ .controls = cs4349_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4349_snd_controls),
+
+ .dapm_widgets = cs4349_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets),
+ .dapm_routes = cs4349_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4349_routes),
+};
+
+static const struct regmap_config cs4349_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4349_MISC,
+ .reg_defaults = cs4349_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults),
+ .readable_reg = cs4349_readable_register,
+ .writeable_reg = cs4349_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs4349_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs4349_private *cs4349;
+ int ret;
+
+ cs4349 = devm_kzalloc(&client->dev, sizeof(*cs4349), GFP_KERNEL);
+ if (!cs4349)
+ return -ENOMEM;
+
+ cs4349->regmap = devm_regmap_init_i2c(client, &cs4349_regmap);
+ if (IS_ERR(cs4349->regmap)) {
+ ret = PTR_ERR(cs4349->regmap);
+ dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs4349->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4349->reset_gpio))
+ return PTR_ERR(cs4349->reset_gpio);
+
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 1);
+
+ i2c_set_clientdata(client, cs4349);
+
+ return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4349,
+ &cs4349_dai, 1);
+}
+
+static int cs4349_i2c_remove(struct i2c_client *client)
+{
+ struct cs4349_private *cs4349 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs4349_runtime_suspend(struct device *dev)
+{
+ struct cs4349_private *cs4349 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, PWR_DWN);
+ if (ret < 0)
+ return ret;
+
+ regcache_cache_only(cs4349->regmap, true);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 0);
+
+ return 0;
+}
+
+static int cs4349_runtime_resume(struct device *dev)
+{
+ struct cs4349_private *cs4349 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, 0);
+ if (ret < 0)
+ return ret;
+
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 1);
+
+ regcache_cache_only(cs4349->regmap, false);
+ regcache_sync(cs4349->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs4349_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs4349_of_match[] = {
+ { .compatible = "cirrus,cs4349", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs4349_of_match);
+
+static const struct i2c_device_id cs4349_i2c_id[] = {
+ {"cs4349", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id);
+
+static struct i2c_driver cs4349_i2c_driver = {
+ .driver = {
+ .name = "cs4349",
+ .of_match_table = cs4349_of_match,
+ },
+ .id_table = cs4349_i2c_id,
+ .probe = cs4349_i2c_probe,
+ .remove = cs4349_i2c_remove,
+};
+
+module_i2c_driver(cs4349_i2c_driver);
+
+MODULE_AUTHOR("Tim Howe <tim.howe@cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS4349 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs4349.h b/kernel/sound/soc/codecs/cs4349.h
new file mode 100644
index 000000000..d58c06a25
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4349.h
@@ -0,0 +1,136 @@
+/*
+ * ALSA SoC CS4349 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Tim Howe <Tim.Howe@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef __CS4349_H__
+#define __CS4349_H__
+
+/* CS4349 registers addresses */
+#define CS4349_CHIPID 0x01 /* Device and Rev ID, Read Only */
+#define CS4349_MODE 0x02 /* Mode Control */
+#define CS4349_VMI 0x03 /* Volume, Mixing, Inversion Control */
+#define CS4349_MUTE 0x04 /* Mute Control */
+#define CS4349_VOLA 0x05 /* DAC Channel A Volume Control */
+#define CS4349_VOLB 0x06 /* DAC Channel B Volume Control */
+#define CS4349_RMPFLT 0x07 /* Ramp and Filter Control */
+#define CS4349_MISC 0x08 /* Power Down,Freeze Control,Pop Stop*/
+
+#define CS4349_I2C_INCR 0x80
+
+
+/* Device and Revision ID */
+#define CS4349_REVA 0xF0 /* Rev A */
+#define CS4349_REVB 0xF1 /* Rev B */
+#define CS4349_REVC2 0xFF /* Rev C2 */
+
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define PDN_POLL_MAX 900
+
+
+/* Bitfield Definitions */
+
+/* CS4349_MODE */
+/* (Digital Interface Format, De-Emphasis Control, Functional Mode */
+#define DIF2 (1 << 6)
+#define DIF1 (1 << 5)
+#define DIF0 (1 << 4)
+#define DEM1 (1 << 3)
+#define DEM0 (1 << 2)
+#define FM1 (1 << 1)
+#define DIF_LEFT_JST 0x00
+#define DIF_I2S 0x01
+#define DIF_RGHT_JST16 0x02
+#define DIF_RGHT_JST24 0x03
+#define DIF_TDM0 0x04
+#define DIF_TDM1 0x05
+#define DIF_TDM2 0x06
+#define DIF_TDM3 0x07
+#define DIF_MASK 0x70
+#define MODE_FORMAT(x) (((x)&7)<<4)
+#define DEM_MASK 0x0C
+#define NO_DEM 0x00
+#define DEM_441 0x04
+#define DEM_48K 0x08
+#define DEM_32K 0x0C
+#define FM_AUTO 0x00
+#define FM_SNGL 0x01
+#define FM_DBL 0x02
+#define FM_QUAD 0x03
+#define FM_SNGL_MIN 30000
+#define FM_SNGL_MAX 54000
+#define FM_DBL_MAX 108000
+#define FM_QUAD_MAX 216000
+#define FM_MASK 0x03
+
+/* CS4349_VMI (VMI = Volume, Mixing and Inversion Controls) */
+#define VOLBISA (1 << 7)
+#define VOLAISB (1 << 7)
+/* INVERT_A only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */
+#define INVERT_A (1 << 6)
+/* INVERT_B only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */
+#define INVERT_B (1 << 5)
+#define ATAPI3 (1 << 3)
+#define ATAPI2 (1 << 2)
+#define ATAPI1 (1 << 1)
+#define ATAPI0 (1 << 0)
+#define MUTEAB 0x00
+#define MUTEA_RIGHTB 0x01
+#define MUTEA_LEFTB 0x02
+#define MUTEA_SUMLRDIV2B 0x03
+#define RIGHTA_MUTEB 0x04
+#define RIGHTA_RIGHTB 0x05
+#define RIGHTA_LEFTB 0x06
+#define RIGHTA_SUMLRDIV2B 0x07
+#define LEFTA_MUTEB 0x08
+#define LEFTA_RIGHTB 0x09 /* Default */
+#define LEFTA_LEFTB 0x0A
+#define LEFTA_SUMLRDIV2B 0x0B
+#define SUMLRDIV2A_MUTEB 0x0C
+#define SUMLRDIV2A_RIGHTB 0x0D
+#define SUMLRDIV2A_LEFTB 0x0E
+#define SUMLRDIV2_AB 0x0F
+#define CHMIX_MASK 0x0F
+
+/* CS4349_MUTE */
+#define AUTOMUTE (1 << 7)
+#define MUTEC_AB (1 << 5)
+#define MUTE_A (1 << 4)
+#define MUTE_B (1 << 3)
+#define MUTE_AB_MASK 0x18
+
+/* CS4349_RMPFLT (Ramp and Filter Control) */
+#define SCZ1 (1 << 7)
+#define SCZ0 (1 << 6)
+#define RMP_UP (1 << 5)
+#define RMP_DN (1 << 4)
+#define FILT_SEL (1 << 2)
+#define IMMDT_CHNG 0x31
+#define ZEROCRSS 0x71
+#define SOFT_RMP 0xB1
+#define SFTRMP_ZEROCRSS 0xF1
+#define SR_ZC_MASK 0xC0
+
+/* CS4349_MISC */
+#define PWR_DWN (1 << 7)
+#define FREEZE (1 << 5)
+#define POPG_EN (1 << 4)
+
+#endif /* __CS4349_H__ */
diff --git a/kernel/sound/soc/codecs/cx20442.c b/kernel/sound/soc/codecs/cx20442.c
index 0f334bc1b..d6f4abbbf 100644
--- a/kernel/sound/soc/codecs/cx20442.c
+++ b/kernel/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/kernel/sound/soc/codecs/da7210.c b/kernel/sound/soc/codecs/da7210.c
index 21810e5f3..af23a61b7 100644
--- a/kernel/sound/soc/codecs/da7210.c
+++ b/kernel/sound/soc/codecs/da7210.c
@@ -267,33 +267,29 @@ enum clk_src {
*
* Reserved area are considered as "mute".
*/
-static const unsigned int hp_out_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(hp_out_tlv,
0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* -54 dB to +15 dB */
- 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0),
-};
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
+);
-static const unsigned int lineout_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(lineout_vol_tlv,
0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* -54dB to 15dB */
0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
-};
+);
-static const unsigned int mono_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(mono_vol_tlv,
0x0, 0x2, TLV_DB_SCALE_ITEM(-1800, 0, 1),
/* -18dB to 6dB */
0x3, 0x7, TLV_DB_SCALE_ITEM(-1800, 600, 0)
-};
+);
-static const unsigned int aux1_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(aux1_vol_tlv,
0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* -48dB to 21dB */
0x11, 0x3f, TLV_DB_SCALE_ITEM(-4800, 150, 0)
-};
+);
static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(adc_eq_master_gain_tlv, -1800, 600, 1);
@@ -680,7 +676,7 @@ struct da7210_priv {
int master;
};
-static struct reg_default da7210_reg_defaults[] = {
+static const struct reg_default da7210_reg_defaults[] = {
{ 0x00, 0x00 },
{ 0x01, 0x11 },
{ 0x03, 0x00 },
@@ -1182,7 +1178,7 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
#if IS_ENABLED(CONFIG_I2C)
-static struct reg_default da7210_regmap_i2c_patch[] = {
+static const struct reg_sequence da7210_regmap_i2c_patch[] = {
/* System controller master disable */
{ DA7210_STARTUP1, 0x00 },
@@ -1259,7 +1255,6 @@ MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
static struct i2c_driver da7210_i2c_driver = {
.driver = {
.name = "da7210",
- .owner = THIS_MODULE,
},
.probe = da7210_i2c_probe,
.remove = da7210_i2c_remove,
@@ -1269,7 +1264,7 @@ static struct i2c_driver da7210_i2c_driver = {
#if defined(CONFIG_SPI_MASTER)
-static struct reg_default da7210_regmap_spi_patch[] = {
+static const struct reg_sequence da7210_regmap_spi_patch[] = {
/* Dummy read to give two pulses over nCS for SPI */
{ DA7210_AUX2, 0x00 },
{ DA7210_AUX2, 0x00 },
@@ -1344,7 +1339,6 @@ static int da7210_spi_remove(struct spi_device *spi)
static struct spi_driver da7210_spi_driver = {
.driver = {
.name = "da7210",
- .owner = THIS_MODULE,
},
.probe = da7210_spi_probe,
.remove = da7210_spi_remove
diff --git a/kernel/sound/soc/codecs/da7213.c b/kernel/sound/soc/codecs/da7213.c
index 9ec577f0e..7278f9346 100644
--- a/kernel/sound/soc/codecs/da7213.c
+++ b/kernel/sound/soc/codecs/da7213.c
@@ -12,6 +12,7 @@
* option) any later version.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -28,27 +29,24 @@
/* Gain and Volume */
-static const unsigned int aux_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(aux_vol_tlv,
/* -54dB */
0x0, 0x11, TLV_DB_SCALE_ITEM(-5400, 0, 0),
/* -52.5dB to 15dB */
0x12, 0x3f, TLV_DB_SCALE_ITEM(-5250, 150, 0)
-};
+);
-static const unsigned int digital_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(digital_gain_tlv,
0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* -78dB to 12dB */
0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0)
-};
+);
-static const unsigned int alc_analog_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv,
0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* 0dB to 36dB */
0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
-};
+);
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
@@ -954,7 +952,7 @@ static const struct snd_soc_dapm_route da7213_audio_map[] = {
{"LINE", NULL, "Lineout PGA"},
};
-static struct reg_default da7213_reg_defaults[] = {
+static const struct reg_default da7213_reg_defaults[] = {
{ DA7213_DIG_ROUTING_DAI, 0x10 },
{ DA7213_SR, 0x0A },
{ DA7213_REFERENCES, 0x80 },
@@ -1225,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
switch (clk_id) {
case DA7213_CLKSRC_MCLK:
- if ((freq == 32768) ||
- ((freq >= 5000000) && (freq <= 54000000))) {
- da7213->mclk_rate = freq;
- return 0;
- } else {
- dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
- freq);
- return -EINVAL;
- }
+ da7213->mclk_squarer_en = false;
+ break;
+ case DA7213_CLKSRC_MCLK_SQR:
+ da7213->mclk_squarer_en = true;
break;
default:
dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
return -EINVAL;
}
+
+ da7213->clk_src = clk_id;
+
+ if (da7213->mclk) {
+ freq = clk_round_rate(da7213->mclk, freq);
+ ret = clk_set_rate(da7213->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7213->mclk_rate = freq;
+
+ return 0;
}
/* Supported PLL input frequencies are 5MHz - 54MHz. */
@@ -1369,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = {
static int da7213_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
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) {
+ /* MCLK */
+ if (da7213->mclk) {
+ ret = clk_prepare_enable(da7213->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
/* Enable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN,
@@ -1385,16 +1417,127 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
/* Disable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
+
+ /* MCLK */
+ if (da7213->mclk)
+ clk_disable_unprepare(da7213->mclk);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
+/* DT */
+static const struct of_device_id da7213_of_match[] = {
+ { .compatible = "dlg,da7213", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7213_of_match);
+
+static enum da7213_micbias_voltage
+ da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1600:
+ return DA7213_MICBIAS_1_6V;
+ case 2200:
+ return DA7213_MICBIAS_2_2V;
+ case 2500:
+ return DA7213_MICBIAS_2_5V;
+ case 3000:
+ return DA7213_MICBIAS_3_0V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level\n");
+ return DA7213_MICBIAS_2_2V;
+ }
+}
+
+static enum da7213_dmic_data_sel
+ da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "lrise_rfall")) {
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ } else if (!strcmp(str, "lfall_rrise")) {
+ return DA7213_DMIC_DATA_LFALL_RRISE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC data select type\n");
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ }
+}
+
+static enum da7213_dmic_samplephase
+ da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "on_clkedge")) {
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ } else if (!strcmp(str, "between_clkedge")) {
+ return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC sample phase\n");
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ }
+}
+
+static enum da7213_dmic_clk_rate
+ da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1500000:
+ return DA7213_DMIC_CLK_1_5MHZ;
+ case 3000000:
+ return DA7213_DMIC_CLK_3_0MHZ;
+ default:
+ dev_warn(codec->dev, "Invalid DMIC clock rate\n");
+ return DA7213_DMIC_CLK_1_5MHZ;
+ }
+}
+
+static struct da7213_platform_data
+ *da7213_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct da7213_platform_data *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_warn(codec->dev, "Failed to allocate memory for pdata\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0)
+ pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias1_lvl = DA7213_MICBIAS_2_2V;
+
+ if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0)
+ pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias2_lvl = DA7213_MICBIAS_2_2V;
+
+ if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str))
+ pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str);
+ else
+ pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str))
+ pdata->dmic_samplephase =
+ da7213_of_dmic_samplephase(codec, of_str);
+ else
+ pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0)
+ pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32);
+ else
+ pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ;
+
+ return pdata;
+}
+
+
static int da7213_probe(struct snd_soc_codec *codec)
{
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
- struct da7213_platform_data *pdata = da7213->pdata;
/* Default to using ALC auto offset calibration mode. */
snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
@@ -1454,8 +1597,15 @@ static int da7213_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, DA7213_LINE_CTRL,
DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7213->pdata = da7213_of_to_pdata(codec);
+ else
+ da7213->pdata = dev_get_platdata(codec->dev);
+
/* Set platform data values */
if (da7213->pdata) {
+ struct da7213_platform_data *pdata = da7213->pdata;
u8 micbias_lvl = 0, dmic_cfg = 0;
/* Set Mic Bias voltages */
@@ -1507,10 +1657,17 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_DATA_SEL_MASK |
DA7213_DMIC_SAMPLEPHASE_MASK |
DA7213_DMIC_CLK_RATE_MASK, dmic_cfg);
+ }
- /* Set MCLK squaring */
- da7213->mclk_squarer_en = pdata->mclk_squaring;
+ /* Check if MCLK provided */
+ da7213->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7213->mclk)) {
+ if (PTR_ERR(da7213->mclk) != -ENOENT)
+ return PTR_ERR(da7213->mclk);
+ else
+ da7213->mclk = NULL;
}
+
return 0;
}
@@ -1541,7 +1698,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da7213_priv *da7213;
- struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret;
da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
@@ -1549,9 +1705,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
if (!da7213)
return -ENOMEM;
- if (pdata)
- da7213->pdata = pdata;
-
i2c_set_clientdata(i2c, da7213);
da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
@@ -1586,7 +1739,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(da7213_of_match),
},
.probe = da7213_i2c_probe,
.remove = da7213_remove,
diff --git a/kernel/sound/soc/codecs/da7213.h b/kernel/sound/soc/codecs/da7213.h
index 9cb9ddd01..030fd691b 100644
--- a/kernel/sound/soc/codecs/da7213.h
+++ b/kernel/sound/soc/codecs/da7213.h
@@ -13,6 +13,7 @@
#ifndef _DA7213_H
#define _DA7213_H
+#include <linux/clk.h>
#include <linux/regmap.h>
#include <sound/da7213.h>
@@ -504,14 +505,17 @@
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
-enum clk_src {
- DA7213_CLKSRC_MCLK
+enum da7213_clk_src {
+ DA7213_CLKSRC_MCLK = 0,
+ DA7213_CLKSRC_MCLK_SQR,
};
/* Codec private data */
struct da7213_priv {
struct regmap *regmap;
+ struct clk *mclk;
unsigned int mclk_rate;
+ int clk_src;
bool master;
bool mclk_squarer_en;
bool srm_en;
diff --git a/kernel/sound/soc/codecs/da7219-aad.c b/kernel/sound/soc/codecs/da7219-aad.c
new file mode 100644
index 000000000..9459593ee
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7219-aad.c
@@ -0,0 +1,823 @@
+/*
+ * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219.h>
+
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * Detection control
+ */
+
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ da7219->aad->jack = jack;
+ da7219->aad->jack_inserted = false;
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
+
+ /* Enable/Disable jack detection */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ (jack ? DA7219_ACCDET_EN_MASK : 0));
+}
+EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
+
+/*
+ * Button/HPTest work
+ */
+
+static void da7219_aad_btn_det_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, btn_det_work);
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ u8 statusa, micbias_ctrl;
+ bool micbias_up = false;
+ int retries = 0;
+
+ /* Drive headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ /* Make sure mic bias is up */
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ do {
+ statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+ if (statusa & DA7219_MICBIAS_UP_STS_MASK)
+ micbias_up = true;
+ else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
+ msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
+ } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
+
+ if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
+ dev_warn(codec->dev, "Mic bias status check timed out");
+
+ /*
+ * Mic bias pulse required to enable mic, must be done before enabling
+ * button detection to prevent erroneous button readings.
+ */
+ if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
+ /* Pulse higher level voltage */
+ micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
+ snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_LEVEL_MASK,
+ da7219_aad->micbias_pulse_lvl);
+ msleep(da7219_aad->micbias_pulse_time);
+ snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
+
+ }
+
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK,
+ da7219_aad->btn_cfg);
+}
+
+static void da7219_aad_hptest_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, hptest_work);
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ u16 tonegen_freq_hptest;
+ u8 accdet_cfg8;
+ int report = 0;
+
+ /* Lock DAPM and any Kcontrols that are affected by this test */
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&da7219->lock);
+
+ /* Bypass cache so it saves current settings */
+ regcache_cache_bypass(da7219->regmap, true);
+
+ /* Make sure Tone Generator is disabled */
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+ /* Enable HPTest block, 1KOhms check */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
+ DA7219_HPTEST_EN_MASK |
+ DA7219_HPTEST_RES_SEL_1KOHMS);
+
+ /* Set gains to 0db */
+ snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
+ snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
+
+ /* Disable DAC filters, EQs and soft mute */
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+ 0);
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+ 0);
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_MASK, 0);
+
+ /* Enable HP left & right paths */
+ snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+ DA7219_CP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
+ DA7219_DAC_L_SRC_TONEGEN |
+ DA7219_DAC_R_SRC_TONEGEN);
+ snd_soc_update_bits(codec, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
+ DA7219_DAC_L_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
+ DA7219_DAC_R_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK);
+ snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_OUTFILT_ST_1L_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1L);
+ snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
+ DA7219_OUTFILT_ST_1R_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1R);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_MASK,
+ DA7219_MIXOUT_L_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_MASK,
+ DA7219_MIXOUT_R_AMP_EN_MASK);
+ snd_soc_write(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_write(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+
+ /* Configure & start Tone Generator */
+ snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+ regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
+ snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2,
+ DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
+ DA7219_SWG_SEL_SRAMP |
+ DA7219_TONE_GEN_GAIN_MINUS_15DB);
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Grab comparator reading */
+ accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8);
+ if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
+ report |= SND_JACK_HEADPHONE;
+ else
+ report |= SND_JACK_LINEOUT;
+
+ /* Stop tone generator */
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Restore original settings from cache */
+ regcache_mark_dirty(da7219->regmap);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_DROUTING_ST_OUTFILT_1R);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_R_SELECT);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
+ DA7219_DIG_ROUTING_DAC);
+ regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
+ DA7219_DAC_FILTERS5);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
+ DA7219_DAC_FILTERS1);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
+ DA7219_TONE_GEN_ON_PER);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ DA7219_TONE_GEN_FREQ1_U);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
+ DA7219_TONE_GEN_CFG2);
+
+ regcache_cache_bypass(da7219->regmap, false);
+
+ /* Disable HPTest block */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK, 0);
+
+ /* Drive Headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ mutex_unlock(&da7219->lock);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ /*
+ * Only send report if jack hasn't been removed during process,
+ * otherwise it's invalid and we drop it.
+ */
+ if (da7219_aad->jack_inserted)
+ snd_soc_jack_report(da7219_aad->jack, report,
+ SND_JACK_HEADSET | SND_JACK_LINEOUT);
+}
+
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
+{
+ struct da7219_aad_priv *da7219_aad = data;
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 events[DA7219_AAD_IRQ_REG_MAX];
+ u8 statusa;
+ int i, report = 0, mask = 0;
+
+ /* Read current IRQ events */
+ regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
+ return IRQ_NONE;
+
+ /* Read status register for jack insertion & type status */
+ statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+
+ /* Clear events */
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+ events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
+ statusa);
+
+ if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
+ /* Jack Insertion */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_INSERTED_MASK) {
+ report |= SND_JACK_MECHANICAL;
+ mask |= SND_JACK_MECHANICAL;
+ da7219_aad->jack_inserted = true;
+ }
+
+ /* Jack type detection */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_DETECT_COMPLETE_MASK) {
+ /*
+ * If 4-pole, then enable button detection, else perform
+ * HP impedance test to determine output type to report.
+ *
+ * We schedule work here as the tasks themselves can
+ * take time to complete, and in particular for hptest
+ * we want to be able to check if the jack was removed
+ * during the procedure as this will invalidate the
+ * result. By doing this as work, the IRQ thread can
+ * handle a removal, and we can check at the end of
+ * hptest if we have a valid result or not.
+ */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ report |= SND_JACK_HEADSET;
+ mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
+ schedule_work(&da7219_aad->btn_det_work);
+ } else {
+ schedule_work(&da7219_aad->hptest_work);
+ }
+ }
+
+ /* Button support for 4-pole jack */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Press */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
+ report |= SND_JACK_BTN_0 >> i;
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Release */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
+ report &= ~(SND_JACK_BTN_0 >> i);
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ }
+ } else {
+ /* Jack removal */
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
+ report = 0;
+ mask |= DA7219_AAD_REPORT_ALL_MASK;
+ da7219_aad->jack_inserted = false;
+
+ /* Un-drive headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK, 0);
+
+ /* Ensure button detection disabled */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ /* Disable mic bias */
+ snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ /* Cancel any pending work */
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+ }
+ }
+
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DT to pdata conversion
+ */
+
+static enum da7219_aad_micbias_pulse_lvl
+ da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 2800:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
+ case 2900:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias pulse level");
+ return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+ }
+}
+
+static enum da7219_aad_btn_cfg
+ da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 2:
+ return DA7219_AAD_BTN_CFG_2MS;
+ case 5:
+ return DA7219_AAD_BTN_CFG_5MS;
+ case 10:
+ return DA7219_AAD_BTN_CFG_10MS;
+ case 50:
+ return DA7219_AAD_BTN_CFG_50MS;
+ case 100:
+ return DA7219_AAD_BTN_CFG_100MS;
+ case 200:
+ return DA7219_AAD_BTN_CFG_200MS;
+ case 500:
+ return DA7219_AAD_BTN_CFG_500MS;
+ default:
+ dev_warn(codec->dev, "Invalid button config");
+ return DA7219_AAD_BTN_CFG_10MS;
+ }
+}
+
+static enum da7219_aad_mic_det_thr
+ da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 200:
+ return DA7219_AAD_MIC_DET_THR_200_OHMS;
+ case 500:
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ case 750:
+ return DA7219_AAD_MIC_DET_THR_750_OHMS;
+ case 1000:
+ return DA7219_AAD_MIC_DET_THR_1000_OHMS;
+ default:
+ dev_warn(codec->dev, "Invalid mic detect threshold");
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ }
+}
+
+static enum da7219_aad_jack_ins_deb
+ da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 5:
+ return DA7219_AAD_JACK_INS_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_INS_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ case 50:
+ return DA7219_AAD_JACK_INS_DEB_50MS;
+ case 100:
+ return DA7219_AAD_JACK_INS_DEB_100MS;
+ case 200:
+ return DA7219_AAD_JACK_INS_DEB_200MS;
+ case 500:
+ return DA7219_AAD_JACK_INS_DEB_500MS;
+ case 1000:
+ return DA7219_AAD_JACK_INS_DEB_1S;
+ default:
+ dev_warn(codec->dev, "Invalid jack insert debounce");
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ }
+}
+
+static enum da7219_aad_jack_det_rate
+ da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "32ms_64ms")) {
+ return DA7219_AAD_JACK_DET_RATE_32_64MS;
+ } else if (!strcmp(str, "64ms_128ms")) {
+ return DA7219_AAD_JACK_DET_RATE_64_128MS;
+ } else if (!strcmp(str, "128ms_256ms")) {
+ return DA7219_AAD_JACK_DET_RATE_128_256MS;
+ } else if (!strcmp(str, "256ms_512ms")) {
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ } else {
+ dev_warn(codec->dev, "Invalid jack detect rate");
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ }
+}
+
+static enum da7219_aad_jack_rem_deb
+ da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ case 5:
+ return DA7219_AAD_JACK_REM_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_REM_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_REM_DEB_20MS;
+ default:
+ dev_warn(codec->dev, "Invalid jack removal debounce");
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ }
+}
+
+static enum da7219_aad_btn_avg
+ da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_BTN_AVG_1;
+ case 2:
+ return DA7219_AAD_BTN_AVG_2;
+ case 4:
+ return DA7219_AAD_BTN_AVG_4;
+ case 8:
+ return DA7219_AAD_BTN_AVG_8;
+ default:
+ dev_warn(codec->dev, "Invalid button average value");
+ return DA7219_AAD_BTN_AVG_2;
+ }
+}
+
+static enum da7219_aad_adc_1bit_rpt
+ da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ case 2:
+ return DA7219_AAD_ADC_1BIT_RPT_2;
+ case 4:
+ return DA7219_AAD_ADC_1BIT_RPT_4;
+ case 8:
+ return DA7219_AAD_ADC_1BIT_RPT_8;
+ default:
+ dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ }
+}
+
+static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+ struct da7219_aad_pdata *aad_pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ if (!aad_np)
+ return NULL;
+
+ aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
+ if (!aad_pdata)
+ goto out;
+
+ aad_pdata->irq = irq_of_parse_and_map(np, 0);
+
+ if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+ &of_val32) >= 0)
+ aad_pdata->micbias_pulse_lvl =
+ da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+ else
+ aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+
+ if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+ &of_val32) >= 0)
+ aad_pdata->micbias_pulse_time = of_val32;
+
+ if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
+ aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+ else
+ aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
+
+ if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+ aad_pdata->mic_det_thr =
+ da7219_aad_of_mic_det_thr(codec, of_val32);
+ else
+ aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+
+ if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+ aad_pdata->jack_ins_deb =
+ da7219_aad_of_jack_ins_deb(codec, of_val32);
+ else
+ aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
+
+ if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+ aad_pdata->jack_det_rate =
+ da7219_aad_of_jack_det_rate(codec, of_str);
+ else
+ aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
+
+ if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+ aad_pdata->jack_rem_deb =
+ da7219_aad_of_jack_rem_deb(codec, of_val32);
+ else
+ aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
+
+ if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
+ aad_pdata->a_d_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->a_d_btn_thr = 0xA;
+
+ if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
+ aad_pdata->d_b_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->d_b_btn_thr = 0x16;
+
+ if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
+ aad_pdata->b_c_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->b_c_btn_thr = 0x21;
+
+ if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
+ aad_pdata->c_mic_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->c_mic_btn_thr = 0x3E;
+
+ if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
+ aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+ else
+ aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
+
+ if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+ aad_pdata->adc_1bit_rpt =
+ da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+ else
+ aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+
+out:
+ of_node_put(aad_np);
+
+ return aad_pdata;
+}
+
+static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if ((pdata) && (pdata->aad_pdata)) {
+ struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
+ u8 cfg, mask;
+
+ da7219_aad->irq = aad_pdata->irq;
+
+ switch (aad_pdata->micbias_pulse_lvl) {
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
+ da7219_aad->micbias_pulse_lvl =
+ (aad_pdata->micbias_pulse_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
+
+ switch (aad_pdata->btn_cfg) {
+ case DA7219_AAD_BTN_CFG_2MS:
+ case DA7219_AAD_BTN_CFG_5MS:
+ case DA7219_AAD_BTN_CFG_10MS:
+ case DA7219_AAD_BTN_CFG_50MS:
+ case DA7219_AAD_BTN_CFG_100MS:
+ case DA7219_AAD_BTN_CFG_200MS:
+ case DA7219_AAD_BTN_CFG_500MS:
+ da7219_aad->btn_cfg = (aad_pdata->btn_cfg <<
+ DA7219_BUTTON_CONFIG_SHIFT);
+ }
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->mic_det_thr) {
+ case DA7219_AAD_MIC_DET_THR_200_OHMS:
+ case DA7219_AAD_MIC_DET_THR_500_OHMS:
+ case DA7219_AAD_MIC_DET_THR_750_OHMS:
+ case DA7219_AAD_MIC_DET_THR_1000_OHMS:
+ cfg |= (aad_pdata->mic_det_thr <<
+ DA7219_MIC_DET_THRESH_SHIFT);
+ mask |= DA7219_MIC_DET_THRESH_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->jack_ins_deb) {
+ case DA7219_AAD_JACK_INS_DEB_5MS:
+ case DA7219_AAD_JACK_INS_DEB_10MS:
+ case DA7219_AAD_JACK_INS_DEB_20MS:
+ case DA7219_AAD_JACK_INS_DEB_50MS:
+ case DA7219_AAD_JACK_INS_DEB_100MS:
+ case DA7219_AAD_JACK_INS_DEB_200MS:
+ case DA7219_AAD_JACK_INS_DEB_500MS:
+ case DA7219_AAD_JACK_INS_DEB_1S:
+ cfg |= (aad_pdata->jack_ins_deb <<
+ DA7219_JACKDET_DEBOUNCE_SHIFT);
+ mask |= DA7219_JACKDET_DEBOUNCE_MASK;
+ }
+ switch (aad_pdata->jack_det_rate) {
+ case DA7219_AAD_JACK_DET_RATE_32_64MS:
+ case DA7219_AAD_JACK_DET_RATE_64_128MS:
+ case DA7219_AAD_JACK_DET_RATE_128_256MS:
+ case DA7219_AAD_JACK_DET_RATE_256_512MS:
+ cfg |= (aad_pdata->jack_det_rate <<
+ DA7219_JACK_DETECT_RATE_SHIFT);
+ mask |= DA7219_JACK_DETECT_RATE_MASK;
+ }
+ switch (aad_pdata->jack_rem_deb) {
+ case DA7219_AAD_JACK_REM_DEB_1MS:
+ case DA7219_AAD_JACK_REM_DEB_5MS:
+ case DA7219_AAD_JACK_REM_DEB_10MS:
+ case DA7219_AAD_JACK_REM_DEB_20MS:
+ cfg |= (aad_pdata->jack_rem_deb <<
+ DA7219_JACKDET_REM_DEB_SHIFT);
+ mask |= DA7219_JACKDET_REM_DEB_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
+
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
+ aad_pdata->a_d_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
+ aad_pdata->d_b_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
+ aad_pdata->b_c_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
+ aad_pdata->c_mic_btn_thr);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->btn_avg) {
+ case DA7219_AAD_BTN_AVG_1:
+ case DA7219_AAD_BTN_AVG_2:
+ case DA7219_AAD_BTN_AVG_4:
+ case DA7219_AAD_BTN_AVG_8:
+ cfg |= (aad_pdata->btn_avg <<
+ DA7219_BUTTON_AVERAGE_SHIFT);
+ mask |= DA7219_BUTTON_AVERAGE_MASK;
+ }
+ switch (aad_pdata->adc_1bit_rpt) {
+ case DA7219_AAD_ADC_1BIT_RPT_1:
+ case DA7219_AAD_ADC_1BIT_RPT_2:
+ case DA7219_AAD_ADC_1BIT_RPT_4:
+ case DA7219_AAD_ADC_1BIT_RPT_8:
+ cfg |= (aad_pdata->adc_1bit_rpt <<
+ DA7219_ADC_1_BIT_REPEAT_SHIFT);
+ mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
+ }
+}
+
+
+/*
+ * Init/Exit
+ */
+
+int da7219_aad_init(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+ int ret;
+
+ da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
+ if (!da7219_aad)
+ return -ENOMEM;
+
+ da7219->aad = da7219_aad;
+ da7219_aad->codec = codec;
+
+ /* Handle any DT/platform data */
+ if ((codec->dev->of_node) && (da7219->pdata))
+ da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+
+ da7219_aad_handle_pdata(codec);
+
+ /* Disable button detection */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
+ INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
+
+ ret = request_threaded_irq(da7219_aad->irq, NULL,
+ da7219_aad_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7219-aad", da7219_aad);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask AAD IRQs */
+ memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ &mask, DA7219_AAD_IRQ_REG_MAX);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da7219_aad_init);
+
+void da7219_aad_exit(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+
+ /* Mask off AAD IRQs */
+ memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ mask, DA7219_AAD_IRQ_REG_MAX);
+
+ free_irq(da7219_aad->irq, da7219_aad);
+
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+}
+EXPORT_SYMBOL_GPL(da7219_aad_exit);
+
+MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/da7219-aad.h b/kernel/sound/soc/codecs/da7219-aad.h
new file mode 100644
index 000000000..4fccf677c
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7219-aad.h
@@ -0,0 +1,212 @@
+/*
+ * da7219-aad.h - DA7322 ASoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __DA7219_AAD_H
+#define __DA7219_AAD_H
+
+#include <linux/timer.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219-aad.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_ACCDET_STATUS_A 0xC0
+#define DA7219_ACCDET_STATUS_B 0xC1
+#define DA7219_ACCDET_IRQ_EVENT_A 0xC2
+#define DA7219_ACCDET_IRQ_EVENT_B 0xC3
+#define DA7219_ACCDET_IRQ_MASK_A 0xC4
+#define DA7219_ACCDET_IRQ_MASK_B 0xC5
+#define DA7219_ACCDET_CONFIG_1 0xC6
+#define DA7219_ACCDET_CONFIG_2 0xC7
+#define DA7219_ACCDET_CONFIG_3 0xC8
+#define DA7219_ACCDET_CONFIG_4 0xC9
+#define DA7219_ACCDET_CONFIG_5 0xCA
+#define DA7219_ACCDET_CONFIG_6 0xCB
+#define DA7219_ACCDET_CONFIG_7 0xCC
+#define DA7219_ACCDET_CONFIG_8 0xCD
+
+
+/*
+ * Bit Fields
+ */
+
+/* DA7219_ACCDET_STATUS_A = 0xC0 */
+#define DA7219_JACK_INSERTION_STS_SHIFT 0
+#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0)
+#define DA7219_JACK_TYPE_STS_SHIFT 1
+#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1)
+#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2
+#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2)
+#define DA7219_MICBIAS_UP_STS_SHIFT 3
+#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3)
+
+/* DA7219_ACCDET_STATUS_B = 0xC1 */
+#define DA7219_BUTTON_TYPE_STS_SHIFT 0
+#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */
+#define DA7219_E_JACK_INSERTED_SHIFT 0
+#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_E_JACK_REMOVED_SHIFT 1
+#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */
+#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */
+#define DA7219_M_JACK_INSERTED_SHIFT 0
+#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_M_JACK_REMOVED_SHIFT 1
+#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */
+#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_1 = 0xC6 */
+#define DA7219_ACCDET_EN_SHIFT 0
+#define DA7219_ACCDET_EN_MASK (0x1 << 0)
+#define DA7219_BUTTON_CONFIG_SHIFT 1
+#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1)
+#define DA7219_MIC_DET_THRESH_SHIFT 4
+#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4)
+#define DA7219_JACK_TYPE_DET_EN_SHIFT 6
+#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6)
+#define DA7219_PIN_ORDER_DET_EN_SHIFT 7
+#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_2 = 0xC7 */
+#define DA7219_ACCDET_PAUSE_SHIFT 0
+#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0)
+#define DA7219_JACKDET_DEBOUNCE_SHIFT 1
+#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1)
+#define DA7219_JACK_DETECT_RATE_SHIFT 4
+#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4)
+#define DA7219_JACKDET_REM_DEB_SHIFT 6
+#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6)
+
+/* DA7219_ACCDET_CONFIG_3 = 0xC8 */
+#define DA7219_A_D_BUTTON_THRESH_SHIFT 0
+#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_4 = 0xC9 */
+#define DA7219_D_B_BUTTON_THRESH_SHIFT 0
+#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_5 = 0xCA */
+#define DA7219_B_C_BUTTON_THRESH_SHIFT 0
+#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_6 = 0xCB */
+#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0
+#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_7 = 0xCC */
+#define DA7219_BUTTON_AVERAGE_SHIFT 0
+#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0)
+#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2
+#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2)
+#define DA7219_PIN_ORDER_FORCE_SHIFT 4
+#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4)
+#define DA7219_JACK_TYPE_FORCE_SHIFT 5
+#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5)
+
+/* DA7219_ACCDET_CONFIG_8 = 0xCD */
+#define DA7219_HPTEST_EN_SHIFT 0
+#define DA7219_HPTEST_EN_MASK (0x1 << 0)
+#define DA7219_HPTEST_RES_SEL_SHIFT 1
+#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1)
+#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1)
+#define DA7219_HPTEST_COMP_SHIFT 4
+#define DA7219_HPTEST_COMP_MASK (0x1 << 4)
+
+
+#define DA7219_AAD_MAX_BUTTONS 4
+#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \
+ SND_JACK_HEADSET | SND_JACK_LINEOUT | \
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+#define DA7219_AAD_MICBIAS_CHK_DELAY 10
+#define DA7219_AAD_MICBIAS_CHK_RETRIES 5
+
+#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28
+#define DA7219_AAD_HPTEST_PERIOD 65
+
+enum da7219_aad_event_regs {
+ DA7219_AAD_IRQ_REG_A = 0,
+ DA7219_AAD_IRQ_REG_B,
+ DA7219_AAD_IRQ_REG_MAX,
+};
+
+/* Private data */
+struct da7219_aad_priv {
+ struct snd_soc_codec *codec;
+ int irq;
+
+ u8 micbias_pulse_lvl;
+ u32 micbias_pulse_time;
+
+ u8 btn_cfg;
+
+ struct work_struct btn_det_work;
+ struct work_struct hptest_work;
+
+ struct snd_soc_jack *jack;
+ bool jack_inserted;
+};
+
+/* AAD control */
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+/* Init/Exit */
+int da7219_aad_init(struct snd_soc_codec *codec);
+void da7219_aad_exit(struct snd_soc_codec *codec);
+
+#endif /* __DA7219_AAD_H */
diff --git a/kernel/sound/soc/codecs/da7219.c b/kernel/sound/soc/codecs/da7219.c
new file mode 100644
index 000000000..f238c1e8a
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7219.c
@@ -0,0 +1,1955 @@
+/*
+ * da7219.c - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.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 <asm/div64.h>
+
+#include <sound/da7219.h>
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0);
+
+static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv,
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -77.25dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0);
+
+/* Input Enums */
+static const char * const da7219_alc_attack_rate_txt[] = {
+ "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs",
+ "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs",
+ "30024/fs"
+};
+
+static const struct soc_enum da7219_alc_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT,
+ DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt);
+
+static const char * const da7219_alc_release_rate_txt[] = {
+ "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs",
+ "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs"
+};
+
+static const struct soc_enum da7219_alc_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT,
+ DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt);
+
+static const char * const da7219_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da7219_alc_hold_time =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT,
+ DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt);
+
+static const char * const da7219_alc_env_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7219_alc_env_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const struct soc_enum da7219_alc_env_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const char * const da7219_alc_anticlip_step_txt[] = {
+ "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs"
+};
+
+static const struct soc_enum da7219_alc_anticlip_step =
+ SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTICLIP_STEP_SHIFT,
+ DA7219_ALC_ANTICLIP_STEP_MAX,
+ da7219_alc_anticlip_step_txt);
+
+/* Input/Output Enums */
+static const char * const da7219_gain_ramp_rate_txt[] = {
+ "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+ "Nominal Rate / 16"
+};
+
+static const struct soc_enum da7219_gain_ramp_rate =
+ SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT,
+ DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt);
+
+static const char * const da7219_hpf_mode_txt[] = {
+ "Disabled", "Audio", "Voice"
+};
+
+static const unsigned int da7219_hpf_mode_val[] = {
+ DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7219_adc_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const struct soc_enum da7219_dac_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const char * const da7219_audio_hpf_corner_txt[] = {
+ "2Hz", "4Hz", "8Hz", "16Hz"
+};
+
+static const struct soc_enum da7219_adc_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const char * const da7219_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da7219_adc_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const char * const da7219_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7219_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT,
+ DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt);
+
+static const char * const da7219_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7219_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT,
+ DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7219_dac_softmute_rate_txt[] = {
+ "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples",
+ "32 Samples", "64 Samples"
+};
+
+static const struct soc_enum da7219_dac_softmute_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT,
+ DA7219_DAC_SOFTMUTE_RATE_MAX,
+ da7219_dac_softmute_rate_txt);
+
+static const char * const da7219_dac_ng_setup_time_txt[] = {
+ "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7219_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_SETUP_TIME_SHIFT,
+ DA7219_DAC_NG_SETUP_TIME_MAX,
+ da7219_dac_ng_setup_time_txt);
+
+static const char * const da7219_dac_ng_rampup_txt[] = {
+ "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPUP_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampup_txt);
+
+static const char * const da7219_dac_ng_rampdown_txt[] = {
+ "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPDN_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampdown_txt);
+
+
+static const char * const da7219_cp_track_mode_txt[] = {
+ "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7219_cp_track_mode_val[] = {
+ DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL,
+ DA7219_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7219_cp_track_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT,
+ DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX,
+ da7219_cp_track_mode_txt,
+ da7219_cp_track_mode_val);
+
+
+/*
+ * Control Functions
+ */
+
+/* Locked Kcontrol calls */
+static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+/* ALC */
+static void da7219_alc_calib(struct snd_soc_codec *codec)
+{
+ u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl;
+
+ /* Save current state of mic control register */
+ mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL);
+
+ /* Save current state of input mixer control register */
+ mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL);
+
+ /* Save current state of input ADC control register */
+ adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL);
+
+ /* Enable then Mute MIC PGAs */
+ snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+ DA7219_MIC_1_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK);
+
+ /* Enable input mixers unmuted */
+ snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_MASK |
+ DA7219_MIXIN_L_AMP_MUTE_EN_MASK,
+ DA7219_MIXIN_L_AMP_EN_MASK);
+
+ /* Enable input filters unmuted */
+ snd_soc_update_bits(codec, DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK,
+ DA7219_ADC_L_EN_MASK);
+
+ /* Perform auto calibration */
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_AUTO_CALIB_EN_MASK,
+ DA7219_ALC_AUTO_CALIB_EN_MASK);
+ do {
+ calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1);
+ } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK);
+
+ /* If auto calibration fails, disable DC offset, hybrid ALC */
+ if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) {
+ dev_warn(codec->dev,
+ "ALC auto calibration failed with overflow\n");
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK, 0);
+ } else {
+ /* Enable DC offset cancellation, hybrid mode */
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK);
+ }
+
+ /* Restore input filter control register to original state */
+ snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl);
+
+ /* Restore input mixer control registers to original state */
+ snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+
+ /* Restore MIC control registers to original states */
+ snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl);
+}
+
+static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ /*
+ * If ALC in operation and value of control has been updated,
+ * make sure calibrated offsets are updated.
+ */
+ if ((ret == 1) && (da7219->alc_en))
+ da7219_alc_calib(codec);
+
+ return ret;
+}
+
+static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+
+ /* Force ALC offset calibration if enabling ALC */
+ if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) {
+ da7219_alc_calib(codec);
+ da7219->alc_en = true;
+ } else {
+ da7219->alc_en = false;
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->lock);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to host endianness here.
+ */
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to little endian here to align with
+ * HW registers.
+ */
+ val = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+ mutex_lock(&da7219->lock);
+ ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7219_snd_controls[] = {
+ /* Mics */
+ SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN,
+ DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_mic_gain_tlv),
+ SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Mixer Input */
+ SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN,
+ DA7219_MIXIN_L_AMP_GAIN_SHIFT,
+ DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_mixin_gain_put,
+ da7219_mixin_gain_tlv),
+ SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ADC */
+ SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN,
+ DA7219_ADC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_adc_dig_gain_tlv),
+ SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ALC */
+ SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7219_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7219_alc_hold_time),
+ SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate),
+ SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate),
+ SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE,
+ DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN,
+ DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX,
+ DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MIN_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MAX_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step),
+ SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_alc_sw_put),
+
+ /* Input High-Pass Filters */
+ SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode),
+ SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner),
+ SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner),
+
+ /* Sidetone Filter */
+ SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN,
+ DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_sidetone_gain_tlv),
+ SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2,
+ DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put, da7219_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1,
+ DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L,
+ DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L,
+ DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER,
+ DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER,
+ DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT),
+
+ /* Gain ramping */
+ SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate),
+
+ /* DAC High-Pass Filter */
+ SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner),
+ SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner),
+
+ /* DAC 5-Band Equaliser */
+ SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Softmute */
+ SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate),
+ SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Noise Gate */
+ SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate),
+ SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH,
+ DA7219_DAC_NG_OFF_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH,
+ DA7219_DAC_NG_ON_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* DACs */
+ SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_dac_dig_gain_tlv),
+ SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* CP */
+ SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode),
+ SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1,
+ DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX,
+ DA7219_NO_INVERT),
+
+ /* Headphones */
+ SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT,
+ DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_hp_gain_tlv),
+ SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7219_out_sel_txt[] = {
+ "ADC", "Tone Generator", "DAIL", "DAIR"
+};
+
+static const struct soc_enum da7219_out_dail_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dail_sel_mux =
+ SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel);
+
+static const struct soc_enum da7219_out_dair_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dair_sel_mux =
+ SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel);
+
+static const struct soc_enum da7219_out_dacl_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacl_sel_mux =
+ SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel);
+
+static const struct soc_enum da7219_out_dacr_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacr_sel_mux =
+ SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+static const struct snd_kcontrol_new da7219_mixin_controls[] = {
+ SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT,
+ DA7219_MIXIN_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_l_controls[] = {
+ SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_r_controls[] = {
+ SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+#define DA7219_DMIX_ST_CTRLS(reg) \
+ SOC_DAPM_SINGLE("Out FilterL Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Out FilterR Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Sidetone Switch", reg, \
+ DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \
+
+static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+static int da7219_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 pll_ctrl, pll_status;
+ int i = 0;
+ bool srm_lock = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (da7219->master)
+ /* Enable DAI clks for master mode */
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
+
+ /* PC synchronised to DAI */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK, 0);
+
+ /* Slave mode, if SRM not enabled no need for status checks */
+ pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL);
+ if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM)
+ return 0;
+
+ /* Check SRM has locked */
+ do {
+ pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS);
+ if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) {
+ srm_lock = true;
+ } else {
+ ++i;
+ msleep(50);
+ }
+ } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock));
+
+ if (!srm_lock)
+ dev_warn(codec->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* PC free-running */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Disable DAI clks if in master mode */
+ if (da7219->master)
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
+ /* Input Supplies */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC"),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Filters */
+ SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Tone Generator */
+ SND_SOC_DAPM_SIGGEN("TONE"),
+ SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1,
+ DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Sidetone Input */
+ SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT),
+
+ /* Input Mixer Supply */
+ SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Mixer */
+ SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0,
+ da7219_mixin_controls,
+ ARRAY_SIZE(da7219_mixin_controls)),
+
+ /* Input Muxes */
+ SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dail_sel_mux),
+ SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dair_sel_mux),
+
+ /* DAI Supply */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT,
+ DA7219_NO_INVERT, da7219_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Muxes */
+ SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacl_sel_mux),
+ SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacr_sel_mux),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_l_controls,
+ ARRAY_SIZE(da7219_mixout_l_controls)),
+ SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_r_controls,
+ ARRAY_SIZE(da7219_mixout_r_controls)),
+
+ /* Sidetone Mixers */
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_st_out_filtl_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0,
+ 0, da7219_st_out_filtr_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+ SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Output Supplies */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
+ DA7219_NO_INVERT, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+
+/*
+ * DAPM Mux Routes
+ */
+
+#define DA7219_OUT_DAI_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIOUT"}, \
+ {name, "DAIR", "DAIOUT"}
+
+#define DA7219_OUT_DAC_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIIN"}, \
+ {name, "DAIR", "DAIIN"}
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7219_DMIX_ST_ROUTES(name) \
+ {name, "Out FilterL Switch", "Mixer Out FilterL"}, \
+ {name, "Out FilterR Switch", "Mixer Out FilterR"}, \
+ {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7219_audio_map[] = {
+ /* Input paths */
+ {"MIC", NULL, "Mic Bias"},
+ {"Mic PGA", NULL, "MIC"},
+ {"Mixin PGA", NULL, "Mic PGA"},
+ {"ADC", NULL, "Mixin PGA"},
+
+ {"Sidetone Filter", NULL, "ADC"},
+ {"Mixer In", NULL, "Mixer In Supply"},
+ {"Mixer In", "Mic Switch", "ADC"},
+
+ {"Tone Generator", NULL, "TONE"},
+
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"),
+
+ {"DAIOUT", NULL, "Out DAIL Mux"},
+ {"DAIOUT", NULL, "Out DAIR Mux"},
+ {"DAIOUT", NULL, "DAI"},
+
+ /* Output paths */
+ {"DAIIN", NULL, "DAI"},
+
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"),
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"),
+
+ {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"},
+ {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"},
+
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+ {"DACL", NULL, "ST Mixer Out FilterL"},
+ {"DACR", NULL, "ST Mixer Out FilterR"},
+
+ {"Mixout Left PGA", NULL, "DACL"},
+ {"Mixout Right PGA", NULL, "DACR"},
+
+ {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+ {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+
+ {"HPL", NULL, "Headphone Left PGA"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"HPL", NULL, "Charge Pump"},
+ {"HPR", NULL, "Charge Pump"},
+};
+
+
+/*
+ * DAI operations
+ */
+
+static int da7219_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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case DA7219_CLKSRC_MCLK_SQR:
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK,
+ DA7219_PLL_MCLK_SQR_EN_MASK);
+ break;
+ case DA7219_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK, 0);
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ da7219->clk_src = clk_id;
+
+ if (da7219->mclk) {
+ freq = clk_round_rate(da7219->mclk, freq);
+ ret = clk_set_rate(da7219->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7219->mclk_rate = freq;
+
+ return 0;
+}
+
+static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ u8 pll_ctrl, indiv_bits, indiv;
+ u8 pll_frac_top, pll_frac_bot, pll_integer;
+ u32 freq_ref;
+ u64 frac_div;
+
+ /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7219->mclk_rate == 32768) {
+ indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+ } else if (da7219->mclk_rate < 2000000) {
+ dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ } else if (da7219->mclk_rate <= 5000000) {
+ indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 10000000) {
+ indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
+ indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 20000000) {
+ indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
+ indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 40000000) {
+ indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
+ indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 54000000) {
+ indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
+ indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
+ } else {
+ dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7219->mclk_rate / indiv);
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7219_SYSCLK_MCLK:
+ pll_ctrl |= DA7219_PLL_MODE_BYPASS;
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK |
+ DA7219_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7219_SYSCLK_PLL:
+ pll_ctrl |= DA7219_PLL_MODE_NORMAL;
+ break;
+ case DA7219_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7219_PLL_MODE_SRM;
+ break;
+ case DA7219_SYSCLK_PLL_32KHZ:
+ pll_ctrl |= DA7219_PLL_MODE_32KHZ;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid PLL config\n");
+ return -EINVAL;
+ }
+
+ /* Calculate dividers for PLL */
+ pll_integer = fout / freq_ref;
+ frac_div = (u64)(fout % freq_ref) * 8192ULL;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
+
+ /* Write PLL config & dividers */
+ snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer);
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK,
+ pll_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 dai_clk_mode = 0, dai_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7219->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7219->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7219_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dai_ctrl |= DA7219_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default 64 BCLKs per WCLK is supported */
+ dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64;
+
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK |
+ DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK,
+ dai_clk_mode);
+ snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7219_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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 dai_bclks_per_wclk;
+ u16 offset;
+ u32 frame_size;
+
+ /* No channels enabled so disable TDM, revert to 64-bit frames */
+ if (!tx_mask) {
+ snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ DA7219_DAI_BCLKS_PER_WCLK_64);
+ return 0;
+ }
+
+ /* Check we have valid slots */
+ if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
+ dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ DA7219_DAI_TDM_MAX_SLOTS);
+ return -EINVAL;
+ }
+
+ /* Check we have a valid offset given */
+ if (rx_mask > DA7219_DAI_OFFSET_MAX) {
+ dev_err(codec->dev, "Invalid slot offset, max = %d\n",
+ DA7219_DAI_OFFSET_MAX);
+ return -EINVAL;
+ }
+
+ /* Calculate & validate frame size based on slot info provided. */
+ frame_size = slots * slot_width;
+ switch (frame_size) {
+ case 32:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid frame size %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ dai_bclks_per_wclk);
+
+ offset = cpu_to_le16(rx_mask);
+ regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
+ &offset, sizeof(offset));
+
+ snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK,
+ (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+ DA7219_DAI_TDM_MODE_EN_MASK);
+
+ return 0;
+}
+
+static int da7219_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;
+ u8 dai_ctrl = 0, fs;
+ unsigned int channels;
+
+ switch (params_width(params)) {
+ case 16:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
+ dev_err(codec->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7219_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7219_SR_8000;
+ break;
+ case 11025:
+ fs = DA7219_SR_11025;
+ break;
+ case 12000:
+ fs = DA7219_SR_12000;
+ break;
+ case 16000:
+ fs = DA7219_SR_16000;
+ break;
+ case 22050:
+ fs = DA7219_SR_22050;
+ break;
+ case 24000:
+ fs = DA7219_SR_24000;
+ break;
+ case 32000:
+ fs = DA7219_SR_32000;
+ break;
+ case 44100:
+ fs = DA7219_SR_44100;
+ break;
+ case 48000:
+ fs = DA7219_SR_48000;
+ break;
+ case 88200:
+ fs = DA7219_SR_88200;
+ break;
+ case 96000:
+ fs = DA7219_SR_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7219_DAI_CTRL,
+ DA7219_DAI_WORD_LENGTH_MASK |
+ DA7219_DAI_CH_NUM_MASK,
+ dai_ctrl);
+ snd_soc_write(codec, DA7219_SR, fs);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops da7219_dai_ops = {
+ .hw_params = da7219_hw_params,
+ .set_sysclk = da7219_set_dai_sysclk,
+ .set_pll = da7219_set_dai_pll,
+ .set_fmt = da7219_set_dai_fmt,
+ .set_tdm_slot = da7219_set_dai_tdm_slot,
+};
+
+#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver da7219_dai = {
+ .name = "da7219-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7219_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7219_FORMATS,
+ },
+ .ops = &da7219_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_channels = 1,
+ .symmetric_samplebits = 1,
+};
+
+
+/*
+ * DT
+ */
+
+static const struct of_device_id da7219_of_match[] = {
+ { .compatible = "dlg,da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7219_of_match);
+
+static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
+ u32 val)
+{
+ switch (val) {
+ case 1050:
+ return DA7219_LDO_LVL_SEL_1_05V;
+ case 1100:
+ return DA7219_LDO_LVL_SEL_1_10V;
+ case 1200:
+ return DA7219_LDO_LVL_SEL_1_20V;
+ case 1400:
+ return DA7219_LDO_LVL_SEL_1_40V;
+ default:
+ dev_warn(codec->dev, "Invalid LDO level");
+ return DA7219_LDO_LVL_SEL_1_05V;
+ }
+}
+
+static enum da7219_micbias_voltage
+ da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1800:
+ return DA7219_MICBIAS_1_8V;
+ case 2000:
+ return DA7219_MICBIAS_2_0V;
+ case 2200:
+ return DA7219_MICBIAS_2_2V;
+ case 2400:
+ return DA7219_MICBIAS_2_4V;
+ case 2600:
+ return DA7219_MICBIAS_2_6V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level");
+ return DA7219_MICBIAS_2_2V;
+ }
+}
+
+static enum da7219_mic_amp_in_sel
+ da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "diff")) {
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ } else if (!strcmp(str, "se_p")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_P;
+ } else if (!strcmp(str, "se_n")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_N;
+ } else {
+ dev_warn(codec->dev, "Invalid mic input type selection");
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ }
+}
+
+static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct da7219_pdata *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
+ pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
+
+ if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
+ pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
+
+ if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
+ pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+ else
+ pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
+
+ return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7219_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ /* MCLK */
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK,
+ DA7219_BIAS_EN_MASK);
+
+ /* Enable Internal Digital LDO */
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_EN_MASK,
+ DA7219_LDO_EN_MASK);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Only disable if jack detection not active */
+ if (!da7219->aad->jack) {
+ /* Bypass Internal Digital LDO */
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_EN_MASK, 0);
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK, 0);
+ }
+
+ /* MCLK */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
+ [DA7219_SUPPLY_VDD] = "VDD",
+ [DA7219_SUPPLY_VDDMIC] = "VDDMIC",
+ [DA7219_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7219_handle_supplies(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct regulator *vddio;
+ u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+ int i, ret;
+
+ /* Get required supplies */
+ for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
+ da7219->supplies[i].supply = da7219_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES,
+ da7219->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to get supplies");
+ return ret;
+ }
+
+ /* Determine VDDIO voltage provided */
+ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
+ ret = regulator_get_voltage(vddio);
+ if (ret < 1200000)
+ dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ else if (ret < 2800000)
+ io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+
+ /* Enable main supplies */
+ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies");
+ return ret;
+ }
+
+ /* Ensure device in active mode */
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
+
+ /* Update IO voltage level range */
+ snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl);
+
+ return 0;
+}
+
+static void da7219_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if (pdata) {
+ u8 micbias_lvl = 0;
+
+ /* Internal LDO */
+ switch (pdata->ldo_lvl_sel) {
+ case DA7219_LDO_LVL_SEL_1_05V:
+ case DA7219_LDO_LVL_SEL_1_10V:
+ case DA7219_LDO_LVL_SEL_1_20V:
+ case DA7219_LDO_LVL_SEL_1_40V:
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_LEVEL_SELECT_MASK,
+ (pdata->ldo_lvl_sel <<
+ DA7219_LDO_LEVEL_SELECT_SHIFT));
+ break;
+ }
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias_lvl) {
+ case DA7219_MICBIAS_1_8V:
+ case DA7219_MICBIAS_2_0V:
+ case DA7219_MICBIAS_2_2V:
+ case DA7219_MICBIAS_2_4V:
+ case DA7219_MICBIAS_2_6V:
+ micbias_lvl |= (pdata->micbias_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl);
+
+ /* Mic */
+ switch (pdata->mic_amp_in_sel) {
+ case DA7219_MIC_AMP_IN_SEL_DIFF:
+ case DA7219_MIC_AMP_IN_SEL_SE_P:
+ case DA7219_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_write(codec, DA7219_MIC_1_SELECT,
+ pdata->mic_amp_in_sel);
+ break;
+ }
+ }
+}
+
+static int da7219_probe(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_init(&da7219->lock);
+
+ /* Regulator configuration */
+ ret = da7219_handle_supplies(codec);
+ if (ret)
+ return ret;
+
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7219->pdata = da7219_of_to_pdata(codec);
+ else
+ da7219->pdata = dev_get_platdata(codec->dev);
+
+ da7219_handle_pdata(codec);
+
+ /* Check if MCLK provided */
+ da7219->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7219->mclk)) {
+ if (PTR_ERR(da7219->mclk) != -ENOENT)
+ return PTR_ERR(da7219->mclk);
+ else
+ da7219->mclk = NULL;
+ }
+
+ /* Default PC counter to free-running */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Default gain ramping */
+ snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
+ DA7219_ADC_L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
+ DA7219_DAC_L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
+ DA7219_DAC_R_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_RAMP_EN_MASK,
+ DA7219_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_RAMP_EN_MASK,
+ DA7219_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+
+ /* Initialise AAD block */
+ return da7219_aad_init(codec);
+}
+
+static int da7219_remove(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ da7219_aad_exit(codec);
+
+ /* Supplies */
+ return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7219_suspend(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ /* Put device into standby mode if jack detection disabled */
+ if (!da7219->aad->jack)
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0);
+
+ return 0;
+}
+
+static int da7219_resume(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ /* Put device into active mode if previously pushed to standby */
+ if (!da7219->aad->jack)
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK);
+
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define da7219_suspend NULL
+#define da7219_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_da7219 = {
+ .probe = da7219_probe,
+ .remove = da7219_remove,
+ .suspend = da7219_suspend,
+ .resume = da7219_resume,
+ .set_bias_level = da7219_set_bias_level,
+
+ .controls = da7219_snd_controls,
+ .num_controls = ARRAY_SIZE(da7219_snd_controls),
+
+ .dapm_widgets = da7219_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
+ .dapm_routes = da7219_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
+};
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7219_reg_defaults[] = {
+ { DA7219_MIC_1_SELECT, 0x00 },
+ { DA7219_CIF_TIMEOUT_CTRL, 0x01 },
+ { DA7219_SR_24_48, 0x00 },
+ { DA7219_SR, 0x0A },
+ { DA7219_CIF_I2C_ADDR_CFG, 0x02 },
+ { DA7219_PLL_CTRL, 0x10 },
+ { DA7219_PLL_FRAC_TOP, 0x00 },
+ { DA7219_PLL_FRAC_BOT, 0x00 },
+ { DA7219_PLL_INTEGER, 0x20 },
+ { DA7219_DIG_ROUTING_DAI, 0x10 },
+ { DA7219_DAI_CLK_MODE, 0x01 },
+ { DA7219_DAI_CTRL, 0x28 },
+ { DA7219_DAI_TDM_CTRL, 0x40 },
+ { DA7219_DIG_ROUTING_DAC, 0x32 },
+ { DA7219_DAI_OFFSET_LOWER, 0x00 },
+ { DA7219_DAI_OFFSET_UPPER, 0x00 },
+ { DA7219_REFERENCES, 0x00 },
+ { DA7219_MIXIN_L_SELECT, 0x00 },
+ { DA7219_MIXIN_L_GAIN, 0x03 },
+ { DA7219_ADC_L_GAIN, 0x6F },
+ { DA7219_ADC_FILTERS1, 0x80 },
+ { DA7219_MIC_1_GAIN, 0x01 },
+ { DA7219_SIDETONE_CTRL, 0x40 },
+ { DA7219_SIDETONE_GAIN, 0x0E },
+ { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 },
+ { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 },
+ { DA7219_DAC_FILTERS5, 0x00 },
+ { DA7219_DAC_FILTERS2, 0x88 },
+ { DA7219_DAC_FILTERS3, 0x88 },
+ { DA7219_DAC_FILTERS4, 0x08 },
+ { DA7219_DAC_FILTERS1, 0x80 },
+ { DA7219_DAC_L_GAIN, 0x6F },
+ { DA7219_DAC_R_GAIN, 0x6F },
+ { DA7219_CP_CTRL, 0x20 },
+ { DA7219_HP_L_GAIN, 0x39 },
+ { DA7219_HP_R_GAIN, 0x39 },
+ { DA7219_MIXOUT_L_SELECT, 0x00 },
+ { DA7219_MIXOUT_R_SELECT, 0x00 },
+ { DA7219_MICBIAS_CTRL, 0x03 },
+ { DA7219_MIC_1_CTRL, 0x40 },
+ { DA7219_MIXIN_L_CTRL, 0x40 },
+ { DA7219_ADC_L_CTRL, 0x40 },
+ { DA7219_DAC_L_CTRL, 0x40 },
+ { DA7219_DAC_R_CTRL, 0x40 },
+ { DA7219_HP_L_CTRL, 0x40 },
+ { DA7219_HP_R_CTRL, 0x40 },
+ { DA7219_MIXOUT_L_CTRL, 0x10 },
+ { DA7219_MIXOUT_R_CTRL, 0x10 },
+ { DA7219_CHIP_ID1, 0x23 },
+ { DA7219_CHIP_ID2, 0x93 },
+ { DA7219_CHIP_REVISION, 0x00 },
+ { DA7219_LDO_CTRL, 0x00 },
+ { DA7219_IO_CTRL, 0x00 },
+ { DA7219_GAIN_RAMP_CTRL, 0x00 },
+ { DA7219_PC_COUNT, 0x02 },
+ { DA7219_CP_VOL_THRESHOLD1, 0x0E },
+ { DA7219_DIG_CTRL, 0x00 },
+ { DA7219_ALC_CTRL2, 0x00 },
+ { DA7219_ALC_CTRL3, 0x00 },
+ { DA7219_ALC_NOISE, 0x3F },
+ { DA7219_ALC_TARGET_MIN, 0x3F },
+ { DA7219_ALC_TARGET_MAX, 0x00 },
+ { DA7219_ALC_GAIN_LIMITS, 0xFF },
+ { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7219_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7219_ALC_ANTICLIP_LEVEL, 0x00 },
+ { DA7219_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7219_DAC_NG_OFF_THRESH, 0x00 },
+ { DA7219_DAC_NG_ON_THRESH, 0x00 },
+ { DA7219_DAC_NG_CTRL, 0x00 },
+ { DA7219_TONE_GEN_CFG1, 0x00 },
+ { DA7219_TONE_GEN_CFG2, 0x00 },
+ { DA7219_TONE_GEN_CYCLES, 0x00 },
+ { DA7219_TONE_GEN_FREQ1_L, 0x55 },
+ { DA7219_TONE_GEN_FREQ1_U, 0x15 },
+ { DA7219_TONE_GEN_FREQ2_L, 0x00 },
+ { DA7219_TONE_GEN_FREQ2_U, 0x40 },
+ { DA7219_TONE_GEN_ON_PER, 0x02 },
+ { DA7219_TONE_GEN_OFF_PER, 0x01 },
+ { DA7219_ACCDET_IRQ_MASK_A, 0x00 },
+ { DA7219_ACCDET_IRQ_MASK_B, 0x00 },
+ { DA7219_ACCDET_CONFIG_1, 0xD6 },
+ { DA7219_ACCDET_CONFIG_2, 0x34 },
+ { DA7219_ACCDET_CONFIG_3, 0x0A },
+ { DA7219_ACCDET_CONFIG_4, 0x16 },
+ { DA7219_ACCDET_CONFIG_5, 0x21 },
+ { DA7219_ACCDET_CONFIG_6, 0x3E },
+ { DA7219_ACCDET_CONFIG_7, 0x01 },
+ { DA7219_SYSTEM_ACTIVE, 0x00 },
+};
+
+static bool da7219_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7219_MIC_1_GAIN_STATUS:
+ case DA7219_MIXIN_L_GAIN_STATUS:
+ case DA7219_ADC_L_GAIN_STATUS:
+ case DA7219_DAC_L_GAIN_STATUS:
+ case DA7219_DAC_R_GAIN_STATUS:
+ case DA7219_HP_L_GAIN_STATUS:
+ case DA7219_HP_R_GAIN_STATUS:
+ case DA7219_CIF_CTRL:
+ case DA7219_PLL_SRM_STS:
+ case DA7219_ALC_CTRL1:
+ case DA7219_SYSTEM_MODES_INPUT:
+ case DA7219_SYSTEM_MODES_OUTPUT:
+ case DA7219_ALC_OFFSET_AUTO_M_L:
+ case DA7219_ALC_OFFSET_AUTO_U_L:
+ case DA7219_TONE_GEN_CFG1:
+ case DA7219_ACCDET_STATUS_A:
+ case DA7219_ACCDET_STATUS_B:
+ case DA7219_ACCDET_IRQ_EVENT_A:
+ case DA7219_ACCDET_IRQ_EVENT_B:
+ case DA7219_ACCDET_CONFIG_8:
+ case DA7219_SYSTEM_STATUS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_config da7219_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA7219_SYSTEM_ACTIVE,
+ .reg_defaults = da7219_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults),
+ .volatile_reg = da7219_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static int da7219_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da7219_priv *da7219;
+ int ret;
+
+ da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
+ GFP_KERNEL);
+ if (!da7219)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7219);
+
+ da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
+ if (IS_ERR(da7219->regmap)) {
+ ret = PTR_ERR(da7219->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
+ &da7219_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static int da7219_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id da7219_i2c_id[] = {
+ { "da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7219_i2c_id);
+
+static struct i2c_driver da7219_i2c_driver = {
+ .driver = {
+ .name = "da7219",
+ .of_match_table = of_match_ptr(da7219_of_match),
+ },
+ .probe = da7219_i2c_probe,
+ .remove = da7219_i2c_remove,
+ .id_table = da7219_i2c_id,
+};
+
+module_i2c_driver(da7219_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7219 Codec Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/da7219.h b/kernel/sound/soc/codecs/da7219.h
new file mode 100644
index 000000000..b514268c6
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7219.h
@@ -0,0 +1,820 @@
+/*
+ * da7219.h - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __DA7219_H
+#define __DA7219_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7219.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_MIC_1_GAIN_STATUS 0x6
+#define DA7219_MIXIN_L_GAIN_STATUS 0x8
+#define DA7219_ADC_L_GAIN_STATUS 0xA
+#define DA7219_DAC_L_GAIN_STATUS 0xC
+#define DA7219_DAC_R_GAIN_STATUS 0xD
+#define DA7219_HP_L_GAIN_STATUS 0xE
+#define DA7219_HP_R_GAIN_STATUS 0xF
+#define DA7219_MIC_1_SELECT 0x10
+#define DA7219_CIF_TIMEOUT_CTRL 0x12
+#define DA7219_CIF_CTRL 0x13
+#define DA7219_SR_24_48 0x16
+#define DA7219_SR 0x17
+#define DA7219_CIF_I2C_ADDR_CFG 0x1B
+#define DA7219_PLL_CTRL 0x20
+#define DA7219_PLL_FRAC_TOP 0x22
+#define DA7219_PLL_FRAC_BOT 0x23
+#define DA7219_PLL_INTEGER 0x24
+#define DA7219_PLL_SRM_STS 0x25
+#define DA7219_DIG_ROUTING_DAI 0x2A
+#define DA7219_DAI_CLK_MODE 0x2B
+#define DA7219_DAI_CTRL 0x2C
+#define DA7219_DAI_TDM_CTRL 0x2D
+#define DA7219_DIG_ROUTING_DAC 0x2E
+#define DA7219_ALC_CTRL1 0x2F
+#define DA7219_DAI_OFFSET_LOWER 0x30
+#define DA7219_DAI_OFFSET_UPPER 0x31
+#define DA7219_REFERENCES 0x32
+#define DA7219_MIXIN_L_SELECT 0x33
+#define DA7219_MIXIN_L_GAIN 0x34
+#define DA7219_ADC_L_GAIN 0x36
+#define DA7219_ADC_FILTERS1 0x38
+#define DA7219_MIC_1_GAIN 0x39
+#define DA7219_SIDETONE_CTRL 0x3A
+#define DA7219_SIDETONE_GAIN 0x3B
+#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C
+#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D
+#define DA7219_DAC_FILTERS5 0x40
+#define DA7219_DAC_FILTERS2 0x41
+#define DA7219_DAC_FILTERS3 0x42
+#define DA7219_DAC_FILTERS4 0x43
+#define DA7219_DAC_FILTERS1 0x44
+#define DA7219_DAC_L_GAIN 0x45
+#define DA7219_DAC_R_GAIN 0x46
+#define DA7219_CP_CTRL 0x47
+#define DA7219_HP_L_GAIN 0x48
+#define DA7219_HP_R_GAIN 0x49
+#define DA7219_MIXOUT_L_SELECT 0x4B
+#define DA7219_MIXOUT_R_SELECT 0x4C
+#define DA7219_SYSTEM_MODES_INPUT 0x50
+#define DA7219_SYSTEM_MODES_OUTPUT 0x51
+#define DA7219_MICBIAS_CTRL 0x62
+#define DA7219_MIC_1_CTRL 0x63
+#define DA7219_MIXIN_L_CTRL 0x65
+#define DA7219_ADC_L_CTRL 0x67
+#define DA7219_DAC_L_CTRL 0x69
+#define DA7219_DAC_R_CTRL 0x6A
+#define DA7219_HP_L_CTRL 0x6B
+#define DA7219_HP_R_CTRL 0x6C
+#define DA7219_MIXOUT_L_CTRL 0x6E
+#define DA7219_MIXOUT_R_CTRL 0x6F
+#define DA7219_CHIP_ID1 0x81
+#define DA7219_CHIP_ID2 0x82
+#define DA7219_CHIP_REVISION 0x83
+#define DA7219_LDO_CTRL 0x90
+#define DA7219_IO_CTRL 0x91
+#define DA7219_GAIN_RAMP_CTRL 0x92
+#define DA7219_PC_COUNT 0x94
+#define DA7219_CP_VOL_THRESHOLD1 0x95
+#define DA7219_CP_DELAY 0x96
+#define DA7219_DIG_CTRL 0x99
+#define DA7219_ALC_CTRL2 0x9A
+#define DA7219_ALC_CTRL3 0x9B
+#define DA7219_ALC_NOISE 0x9C
+#define DA7219_ALC_TARGET_MIN 0x9D
+#define DA7219_ALC_TARGET_MAX 0x9E
+#define DA7219_ALC_GAIN_LIMITS 0x9F
+#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA7219_ALC_ANTICLIP_CTRL 0xA1
+#define DA7219_ALC_ANTICLIP_LEVEL 0xA2
+#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3
+#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4
+#define DA7219_DAC_NG_SETUP_TIME 0xAF
+#define DA7219_DAC_NG_OFF_THRESH 0xB0
+#define DA7219_DAC_NG_ON_THRESH 0xB1
+#define DA7219_DAC_NG_CTRL 0xB2
+#define DA7219_TONE_GEN_CFG1 0xB4
+#define DA7219_TONE_GEN_CFG2 0xB5
+#define DA7219_TONE_GEN_CYCLES 0xB6
+#define DA7219_TONE_GEN_FREQ1_L 0xB7
+#define DA7219_TONE_GEN_FREQ1_U 0xB8
+#define DA7219_TONE_GEN_FREQ2_L 0xB9
+#define DA7219_TONE_GEN_FREQ2_U 0xBA
+#define DA7219_TONE_GEN_ON_PER 0xBB
+#define DA7219_TONE_GEN_OFF_PER 0xBC
+#define DA7219_SYSTEM_STATUS 0xE0
+#define DA7219_SYSTEM_ACTIVE 0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7219_SWITCH_EN_MAX 0x1
+
+/* DA7219_MIC_1_GAIN_STATUS = 0x6 */
+#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0)
+#define DA7219_MIC_1_AMP_GAIN_MAX 0x7
+
+/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0)
+
+/* DA7219_ADC_L_GAIN_STATUS = 0xA */
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_L_GAIN_STATUS = 0xC */
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_R_GAIN_STATUS = 0xD */
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_HP_L_GAIN_STATUS = 0xE */
+#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_HP_R_GAIN_STATUS = 0xF */
+#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_MIC_1_SELECT = 0x10 */
+#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0
+#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */
+#define DA7219_I2C_TIMEOUT_EN_SHIFT 0
+#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7219_CIF_CTRL = 0x13 */
+#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0
+#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0)
+#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7
+#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7)
+
+/* DA7219_SR_24_48 = 0x16 */
+#define DA7219_SR_24_48_SHIFT 0
+#define DA7219_SR_24_48_MASK (0x1 << 0)
+
+/* DA7219_SR = 0x17 */
+#define DA7219_SR_SHIFT 0
+#define DA7219_SR_MASK (0xF << 0)
+#define DA7219_SR_8000 (0x01 << 0)
+#define DA7219_SR_11025 (0x02 << 0)
+#define DA7219_SR_12000 (0x03 << 0)
+#define DA7219_SR_16000 (0x05 << 0)
+#define DA7219_SR_22050 (0x06 << 0)
+#define DA7219_SR_24000 (0x07 << 0)
+#define DA7219_SR_32000 (0x09 << 0)
+#define DA7219_SR_44100 (0x0A << 0)
+#define DA7219_SR_48000 (0x0B << 0)
+#define DA7219_SR_88200 (0x0E << 0)
+#define DA7219_SR_96000 (0x0F << 0)
+
+/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */
+#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0
+#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0)
+
+/* DA7219_PLL_CTRL = 0x20 */
+#define DA7219_PLL_INDIV_SHIFT 2
+#define DA7219_PLL_INDIV_MASK (0x7 << 2)
+#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2)
+#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2)
+#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2)
+#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2)
+#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2)
+#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5
+#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5)
+#define DA7219_PLL_MODE_SHIFT 6
+#define DA7219_PLL_MODE_MASK (0x3 << 6)
+#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
+#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
+#define DA7219_PLL_MODE_SRM (0x2 << 6)
+#define DA7219_PLL_MODE_32KHZ (0x3 << 6)
+
+/* DA7219_PLL_FRAC_TOP = 0x22 */
+#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7219_PLL_FRAC_BOT = 0x23 */
+#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7219_PLL_INTEGER = 0x24 */
+#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0)
+
+/* DA7219_PLL_SRM_STS = 0x25 */
+#define DA7219_PLL_SRM_STATE_SHIFT 0
+#define DA7219_PLL_SRM_STATE_MASK (0xF << 0)
+#define DA7219_PLL_SRM_STATUS_SHIFT 4
+#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4)
+#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7)
+
+/* DA7219_DIG_ROUTING_DAI = 0x2A */
+#define DA7219_DAI_L_SRC_SHIFT 0
+#define DA7219_DAI_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAI_R_SRC_SHIFT 4
+#define DA7219_DAI_R_SRC_MASK (0x3 << 4)
+#define DA7219_OUT_SRC_MAX 4
+
+/* DA7219_DAI_CLK_MODE = 0x2B */
+#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0
+#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7219_DAI_CLK_POL_SHIFT 2
+#define DA7219_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7219_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7219_DAI_WCLK_POL_SHIFT 3
+#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7219_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4
+#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7219_DAI_CLK_EN_SHIFT 7
+#define DA7219_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_CTRL = 0x2C */
+#define DA7219_DAI_FORMAT_SHIFT 0
+#define DA7219_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7219_DAI_FORMAT_I2S (0x0 << 0)
+#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7219_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7219_DAI_WORD_LENGTH_SHIFT 2
+#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7219_DAI_CH_NUM_SHIFT 4
+#define DA7219_DAI_CH_NUM_MASK (0x3 << 4)
+#define DA7219_DAI_CH_NUM_MAX 2
+#define DA7219_DAI_EN_SHIFT 7
+#define DA7219_DAI_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_TDM_CTRL = 0x2D */
+#define DA7219_DAI_TDM_CH_EN_SHIFT 0
+#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0)
+#define DA7219_DAI_OE_SHIFT 6
+#define DA7219_DAI_OE_MASK (0x1 << 6)
+#define DA7219_DAI_TDM_MODE_EN_SHIFT 7
+#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7)
+#define DA7219_DAI_TDM_MAX_SLOTS 2
+
+/* DA7219_DIG_ROUTING_DAC = 0x2E */
+#define DA7219_DAC_L_SRC_SHIFT 0
+#define DA7219_DAC_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0)
+#define DA7219_DAC_L_MONO_SHIFT 3
+#define DA7219_DAC_L_MONO_MASK (0x1 << 3)
+#define DA7219_DAC_R_SRC_SHIFT 4
+#define DA7219_DAC_R_SRC_MASK (0x3 << 4)
+#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4)
+#define DA7219_DAC_R_MONO_SHIFT 7
+#define DA7219_DAC_R_MONO_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL1 = 0x2F */
+#define DA7219_ALC_OFFSET_EN_SHIFT 0
+#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0)
+#define DA7219_ALC_SYNC_MODE_SHIFT 1
+#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1)
+#define DA7219_ALC_EN_SHIFT 3
+#define DA7219_ALC_EN_MASK (0x1 << 3)
+#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4
+#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4)
+#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5
+#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5)
+
+/* DA7219_DAI_OFFSET_LOWER = 0x30 */
+#define DA7219_DAI_OFFSET_LOWER_SHIFT 0
+#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0)
+
+/* DA7219_DAI_OFFSET_UPPER = 0x31 */
+#define DA7219_DAI_OFFSET_UPPER_SHIFT 0
+#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0)
+#define DA7219_DAI_OFFSET_MAX 0x2FF
+
+/* DA7219_REFERENCES = 0x32 */
+#define DA7219_BIAS_EN_SHIFT 3
+#define DA7219_BIAS_EN_MASK (0x1 << 3)
+#define DA7219_VMID_FAST_CHARGE_SHIFT 4
+#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4)
+
+/* DA7219_MIXIN_L_SELECT = 0x33 */
+#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXIN_L_GAIN = 0x34 */
+#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0)
+#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF
+
+/* DA7219_ADC_L_GAIN = 0x36 */
+#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F
+
+/* DA7219_ADC_FILTERS1 = 0x38 */
+#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_VOICE_HPF_CORNER_MAX 8
+#define DA7219_ADC_VOICE_EN_SHIFT 3
+#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_AUDIO_HPF_CORNER_MAX 4
+#define DA7219_ADC_HPF_EN_SHIFT 7
+#define DA7219_ADC_HPF_EN_MASK (0x1 << 7)
+#define DA7219_HPF_MODE_SHIFT 0
+#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7))
+#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7))
+#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MAX 3
+
+/* DA7219_MIC_1_GAIN = 0x39 */
+#define DA7219_MIC_1_AMP_GAIN_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0)
+
+/* DA7219_SIDETONE_CTRL = 0x3A */
+#define DA7219_SIDETONE_MUTE_EN_SHIFT 6
+#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_SIDETONE_EN_SHIFT 7
+#define DA7219_SIDETONE_EN_MASK (0x1 << 7)
+
+/* DA7219_SIDETONE_GAIN = 0x3B */
+#define DA7219_SIDETONE_GAIN_SHIFT 0
+#define DA7219_SIDETONE_GAIN_MASK (0xF << 0)
+#define DA7219_SIDETONE_GAIN_MAX 0xE
+
+/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */
+#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0
+#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1
+#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2
+#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1)
+
+/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */
+#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0)
+
+/* DA7219_DAC_FILTERS5 = 0x40 */
+#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4)
+#define DA7219_DAC_SOFTMUTE_RATE_MAX 7
+#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7
+#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS2 = 0x41 */
+#define DA7219_DAC_EQ_BAND1_SHIFT 0
+#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND2_SHIFT 4
+#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4)
+#define DA7219_DAC_EQ_BAND_MAX 0xF
+
+/* DA7219_DAC_FILTERS3 = 0x42 */
+#define DA7219_DAC_EQ_BAND3_SHIFT 0
+#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND4_SHIFT 4
+#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4)
+
+/* DA7219_DAC_FILTERS4 = 0x43 */
+#define DA7219_DAC_EQ_BAND5_SHIFT 0
+#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0)
+#define DA7219_DAC_EQ_EN_SHIFT 7
+#define DA7219_DAC_EQ_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS1 = 0x44 */
+#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_DAC_VOICE_EN_SHIFT 3
+#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_DAC_HPF_EN_SHIFT 7
+#define DA7219_DAC_HPF_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_GAIN = 0x45 */
+#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F
+#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0)
+
+/* DA7219_DAC_R_GAIN = 0x46 */
+#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7219_CP_CTRL = 0x47 */
+#define DA7219_CP_MCHANGE_SHIFT 4
+#define DA7219_CP_MCHANGE_MASK (0x3 << 4)
+#define DA7219_CP_MCHANGE_REL_MASK 0x3
+#define DA7219_CP_MCHANGE_MAX 3
+#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1
+#define DA7219_CP_MCHANGE_DAC_VOL 0x2
+#define DA7219_CP_MCHANGE_SIG_MAG 0x3
+#define DA7219_CP_EN_SHIFT 7
+#define DA7219_CP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_GAIN = 0x48 */
+#define DA7219_HP_L_AMP_GAIN_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0)
+#define DA7219_HP_AMP_GAIN_MAX 0x3F
+#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0)
+
+/* DA7219_HP_R_GAIN = 0x49 */
+#define DA7219_HP_R_AMP_GAIN_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0)
+
+/* DA7219_MIXOUT_L_SELECT = 0x4B */
+#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXOUT_R_SELECT = 0x4C */
+#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_SYSTEM_MODES_INPUT = 0x50 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_ADC_MODE_SHIFT 1
+#define DA7219_ADC_MODE_MASK (0x7F << 1)
+
+/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_DAC_MODE_SHIFT 1
+#define DA7219_DAC_MODE_MASK (0x7F << 1)
+
+/* DA7219_MICBIAS_CTRL = 0x62 */
+#define DA7219_MICBIAS1_LEVEL_SHIFT 0
+#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0)
+#define DA7219_MICBIAS1_EN_SHIFT 3
+#define DA7219_MICBIAS1_EN_MASK (0x1 << 3)
+
+/* DA7219_MIC_1_CTRL = 0x63 */
+#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIC_1_AMP_EN_SHIFT 7
+#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXIN_L_CTRL = 0x65 */
+#define DA7219_MIXIN_L_MIX_EN_SHIFT 3
+#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3)
+#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIXIN_L_AMP_EN_SHIFT 7
+#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_ADC_L_CTRL = 0x67 */
+#define DA7219_ADC_L_BIAS_SHIFT 0
+#define DA7219_ADC_L_BIAS_MASK (0x3 << 0)
+#define DA7219_ADC_L_RAMP_EN_SHIFT 5
+#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_ADC_L_MUTE_EN_SHIFT 6
+#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_ADC_L_EN_SHIFT 7
+#define DA7219_ADC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_CTRL = 0x69 */
+#define DA7219_DAC_L_RAMP_EN_SHIFT 5
+#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_L_MUTE_EN_SHIFT 6
+#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_L_EN_SHIFT 7
+#define DA7219_DAC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_R_CTRL = 0x6A */
+#define DA7219_DAC_R_RAMP_EN_SHIFT 5
+#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_R_MUTE_EN_SHIFT 6
+#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_R_EN_SHIFT 7
+#define DA7219_DAC_R_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_CTRL = 0x6B */
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_L_AMP_OE_SHIFT 3
+#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_L_AMP_EN_SHIFT 7
+#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_R_CTRL = 0x6C */
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_R_AMP_OE_SHIFT 3
+#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_R_AMP_EN_SHIFT 7
+#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_L_CTRL = 0x6E */
+#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_R_CTRL = 0x6F */
+#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_CHIP_ID1 = 0x81 */
+#define DA7219_CHIP_ID1_SHIFT 0
+#define DA7219_CHIP_ID1_MASK (0xFF << 0)
+
+/* DA7219_CHIP_ID2 = 0x82 */
+#define DA7219_CHIP_ID2_SHIFT 0
+#define DA7219_CHIP_ID2_MASK (0xFF << 0)
+
+/* DA7219_CHIP_REVISION = 0x83 */
+#define DA7219_CHIP_MINOR_SHIFT 0
+#define DA7219_CHIP_MINOR_MASK (0xF << 0)
+#define DA7219_CHIP_MAJOR_SHIFT 4
+#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7219_LDO_CTRL = 0x90 */
+#define DA7219_LDO_LEVEL_SELECT_SHIFT 4
+#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4)
+#define DA7219_LDO_EN_SHIFT 7
+#define DA7219_LDO_EN_MASK (0x1 << 7)
+
+/* DA7219_IO_CTRL = 0x91 */
+#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
+#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
+#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0
+#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1
+
+/* DA7219_GAIN_RAMP_CTRL = 0x92 */
+#define DA7219_GAIN_RAMP_RATE_SHIFT 0
+#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7219_GAIN_RAMP_RATE_MAX 4
+
+/* DA7219_PC_COUNT = 0x94 */
+#define DA7219_PC_FREERUN_SHIFT 0
+#define DA7219_PC_FREERUN_MASK (0x1 << 0)
+#define DA7219_PC_RESYNC_AUTO_SHIFT 1
+#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1)
+
+/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */
+#define DA7219_CP_THRESH_VDD2_SHIFT 0
+#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0)
+#define DA7219_CP_THRESH_VDD2_MAX 0x3F
+
+/* DA7219_DIG_CTRL = 0x99 */
+#define DA7219_DAC_L_INV_SHIFT 3
+#define DA7219_DAC_L_INV_MASK (0x1 << 3)
+#define DA7219_DAC_R_INV_SHIFT 7
+#define DA7219_DAC_R_INV_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL2 = 0x9A */
+#define DA7219_ALC_ATTACK_SHIFT 0
+#define DA7219_ALC_ATTACK_MASK (0xF << 0)
+#define DA7219_ALC_ATTACK_MAX 13
+#define DA7219_ALC_RELEASE_SHIFT 4
+#define DA7219_ALC_RELEASE_MASK (0xF << 4)
+#define DA7219_ALC_RELEASE_MAX 11
+
+/* DA7219_ALC_CTRL3 = 0x9B */
+#define DA7219_ALC_HOLD_SHIFT 0
+#define DA7219_ALC_HOLD_MASK (0xF << 0)
+#define DA7219_ALC_HOLD_MAX 16
+#define DA7219_ALC_INTEG_ATTACK_SHIFT 4
+#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4)
+#define DA7219_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6)
+#define DA7219_ALC_INTEG_MAX 4
+
+/* DA7219_ALC_NOISE = 0x9C */
+#define DA7219_ALC_NOISE_SHIFT 0
+#define DA7219_ALC_NOISE_MASK (0x3F << 0)
+#define DA7219_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7219_ALC_TARGET_MIN = 0x9D */
+#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0)
+
+/* DA7219_ALC_TARGET_MAX = 0x9E */
+#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0)
+
+/* DA7219_ALC_GAIN_LIMITS = 0x9F */
+#define DA7219_ALC_ATTEN_MAX_SHIFT 0
+#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0)
+#define DA7219_ALC_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4)
+#define DA7219_ALC_ATTEN_GAIN_MAX 0xF
+
+/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0)
+#define DA7219_ALC_ANA_GAIN_MIN 0x1
+#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4)
+#define DA7219_ALC_ANA_GAIN_MAX 0x7
+
+/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0)
+#define DA7219_ALC_ANTICLIP_STEP_MAX 4
+#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7
+#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7)
+
+/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0
+#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */
+#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */
+#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0)
+
+/* DA7219_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0)
+#define DA7219_DAC_NG_SETUP_TIME_MAX 4
+#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7219_DAC_NG_RAMP_RATE_MAX 2
+
+/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */
+#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0)
+#define DA7219_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7219_DAC_NG_ON_THRESH = 0xB1 */
+#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0)
+
+/* DA7219_DAC_NG_CTRL = 0xB2 */
+#define DA7219_DAC_NG_EN_SHIFT 7
+#define DA7219_DAC_NG_EN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG1 = 0xB4 */
+#define DA7219_DTMF_REG_SHIFT 0
+#define DA7219_DTMF_REG_MASK (0xF << 0)
+#define DA7219_DTMF_REG_MAX 16
+#define DA7219_DTMF_EN_SHIFT 4
+#define DA7219_DTMF_EN_MASK (0x1 << 4)
+#define DA7219_START_STOPN_SHIFT 7
+#define DA7219_START_STOPN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG2 = 0xB5 */
+#define DA7219_SWG_SEL_SHIFT 0
+#define DA7219_SWG_SEL_MASK (0x3 << 0)
+#define DA7219_SWG_SEL_MAX 4
+#define DA7219_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7219_TONE_GEN_GAIN_SHIFT 4
+#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7219_TONE_GEN_GAIN_MAX 0xF
+#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7219_TONE_GEN_CYCLES = 0xB6 */
+#define DA7219_BEEP_CYCLES_SHIFT 0
+#define DA7219_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7219_FREQ1_L_SHIFT 0
+#define DA7219_FREQ1_L_MASK (0xFF << 0)
+#define DA7219_FREQ_MAX 0xFFFF
+
+/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7219_FREQ1_U_SHIFT 0
+#define DA7219_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7219_FREQ2_L_SHIFT 0
+#define DA7219_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7219_FREQ2_U_SHIFT 0
+#define DA7219_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_ON_PER = 0xBB */
+#define DA7219_BEEP_ON_PER_SHIFT 0
+#define DA7219_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7219_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7219_TONE_GEN_OFF_PER = 0xBC */
+#define DA7219_BEEP_OFF_PER_SHIFT 0
+#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0)
+
+/* DA7219_SYSTEM_STATUS = 0xE0 */
+#define DA7219_SC1_BUSY_SHIFT 0
+#define DA7219_SC1_BUSY_MASK (0x1 << 0)
+#define DA7219_SC2_BUSY_SHIFT 1
+#define DA7219_SC2_BUSY_MASK (0x1 << 1)
+
+/* DA7219_SYSTEM_ACTIVE = 0xFD */
+#define DA7219_SYSTEM_ACTIVE_SHIFT 0
+#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7219_NO_INVERT 0
+#define DA7219_INVERT 1
+
+/* Byte related defines */
+#define DA7219_BYTE_SHIFT 8
+#define DA7219_BYTE_MASK 0xFF
+
+/* PLL Output Frequencies */
+#define DA7219_PLL_FREQ_OUT_90316 90316800
+#define DA7219_PLL_FREQ_OUT_98304 98304000
+
+/* PLL Frequency Dividers */
+#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1
+#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2
+#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
+#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
+#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
+
+/* SRM */
+#define DA7219_SRM_CHECK_RETRIES 8
+
+enum da7219_clk_src {
+ DA7219_CLKSRC_MCLK = 0,
+ DA7219_CLKSRC_MCLK_SQR,
+};
+
+enum da7219_sys_clk {
+ DA7219_SYSCLK_MCLK = 0,
+ DA7219_SYSCLK_PLL,
+ DA7219_SYSCLK_PLL_SRM,
+ DA7219_SYSCLK_PLL_32KHZ
+};
+
+/* Regulators */
+enum da7219_supplies {
+ DA7219_SUPPLY_VDD = 0,
+ DA7219_SUPPLY_VDDMIC,
+ DA7219_SUPPLY_VDDIO,
+ DA7219_NUM_SUPPLIES,
+};
+
+struct da7219_aad_priv;
+
+/* Private data */
+struct da7219_priv {
+ struct da7219_aad_priv *aad;
+ struct da7219_pdata *pdata;
+
+ struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct mutex lock;
+
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ int clk_src;
+
+ bool master;
+ bool alc_en;
+};
+
+#endif /* __DA7219_H */
diff --git a/kernel/sound/soc/codecs/da732x.c b/kernel/sound/soc/codecs/da732x.c
index 911c26c70..1d5a89c51 100644
--- a/kernel/sound/soc/codecs/da732x.c
+++ b/kernel/sound/soc/codecs/da732x.c
@@ -43,7 +43,7 @@ struct da732x_priv {
/*
* da732x register cache - default settings
*/
-static struct reg_default da732x_reg_cache[] = {
+static const struct reg_default da732x_reg_cache[] = {
{ DA732X_REG_REF1 , 0x02 },
{ DA732X_REG_BIAS_EN , 0x80 },
{ DA732X_REG_BIAS1 , 0x00 },
@@ -1196,13 +1196,7 @@ static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
#define DA732X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops da732x_dai1_ops = {
- .hw_params = da732x_hw_params,
- .set_fmt = da732x_set_dai_fmt,
- .set_sysclk = da732x_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops da732x_dai2_ops = {
+static const struct snd_soc_dai_ops da732x_dai_ops = {
.hw_params = da732x_hw_params,
.set_fmt = da732x_set_dai_fmt,
.set_sysclk = da732x_set_dai_sysclk,
@@ -1227,7 +1221,7 @@ static struct snd_soc_dai_driver da732x_dai[] = {
.rates = DA732X_RATES,
.formats = DA732X_FORMATS,
},
- .ops = &da732x_dai1_ops,
+ .ops = &da732x_dai_ops,
},
{
.name = "DA732X_AIFB",
@@ -1247,7 +1241,7 @@ static struct snd_soc_dai_driver da732x_dai[] = {
.rates = DA732X_RATES,
.formats = DA732X_FORMATS,
},
- .ops = &da732x_dai2_ops,
+ .ops = &da732x_dai_ops,
},
};
@@ -1432,7 +1426,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 +1496,6 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1574,7 +1566,6 @@ MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
static struct i2c_driver da732x_i2c_driver = {
.driver = {
.name = "da7320",
- .owner = THIS_MODULE,
},
.probe = da732x_i2c_probe,
.remove = da732x_i2c_remove,
diff --git a/kernel/sound/soc/codecs/da9055.c b/kernel/sound/soc/codecs/da9055.c
index ad19cc567..0b2ede8db 100644
--- a/kernel/sound/soc/codecs/da9055.c
+++ b/kernel/sound/soc/codecs/da9055.c
@@ -289,26 +289,23 @@ enum clk_src {
/* Gain and Volume */
-static const unsigned int aux_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(aux_vol_tlv,
0x0, 0x10, TLV_DB_SCALE_ITEM(-5400, 0, 0),
/* -54dB to 15dB */
0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
-};
+);
-static const unsigned int digital_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(digital_gain_tlv,
0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* -78dB to 12dB */
0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0)
-};
+);
-static const unsigned int alc_analog_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv,
0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
/* 0dB to 36dB */
0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
-};
+);
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
@@ -948,7 +945,7 @@ struct da9055_priv {
struct da9055_platform_data *pdata;
};
-static struct reg_default da9055_reg_defaults[] = {
+static const struct reg_default da9055_reg_defaults[] = {
{ 0x21, 0x10 },
{ 0x22, 0x0A },
{ 0x23, 0x00 },
@@ -1364,7 +1361,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 +1374,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;
}
@@ -1534,12 +1530,12 @@ static const struct of_device_id da9055_of_match[] = {
{ .compatible = "dlg,da9055-codec", },
{ }
};
+MODULE_DEVICE_TABLE(of, da9055_of_match);
/* I2C codec control layer */
static struct i2c_driver da9055_i2c_driver = {
.driver = {
.name = "da9055-codec",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(da9055_of_match),
},
.probe = da9055_i2c_probe,
diff --git a/kernel/sound/soc/codecs/es8328.c b/kernel/sound/soc/codecs/es8328.c
index c5f35a07e..afa6c5db9 100644
--- a/kernel/sound/soc/codecs/es8328.c
+++ b/kernel/sound/soc/codecs/es8328.c
@@ -85,7 +85,15 @@ static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
-static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
+static const struct {
+ int rate;
+ unsigned int val;
+} deemph_settings[] = {
+ { 0, ES8328_DACCONTROL6_DEEMPH_OFF },
+ { 32000, ES8328_DACCONTROL6_DEEMPH_32k },
+ { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k },
+ { 48000, ES8328_DACCONTROL6_DEEMPH_48k },
+};
static int es8328_set_deemph(struct snd_soc_codec *codec)
{
@@ -97,21 +105,22 @@ static int es8328_set_deemph(struct snd_soc_codec *codec)
* rate.
*/
if (es8328->deemph) {
- best = 1;
- for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
- if (abs(deemph_settings[i] - es8328->playback_fs) <
- abs(deemph_settings[best] - es8328->playback_fs))
+ best = 0;
+ for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i].rate - es8328->playback_fs) <
+ abs(deemph_settings[best].rate - es8328->playback_fs))
best = i;
}
- val = best << 1;
+ val = deemph_settings[best].val;
} else {
- val = 0;
+ val = ES8328_DACCONTROL6_DEEMPH_OFF;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
+ return snd_soc_update_bits(codec, ES8328_DACCONTROL6,
+ ES8328_DACCONTROL6_DEEMPH_MASK, val);
}
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
@@ -129,7 +138,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret;
if (deemph > 1)
@@ -205,18 +214,18 @@ static const struct snd_kcontrol_new es8328_right_line_controls =
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
- SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
};
static const char * const es8328_pga_sel[] = {
@@ -536,7 +545,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 +575,6 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/kernel/sound/soc/codecs/es8328.h b/kernel/sound/soc/codecs/es8328.h
index cb36afe10..156c748c8 100644
--- a/kernel/sound/soc/codecs/es8328.h
+++ b/kernel/sound/soc/codecs/es8328.h
@@ -153,6 +153,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6)
#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
diff --git a/kernel/sound/soc/codecs/gtm601.c b/kernel/sound/soc/codecs/gtm601.c
new file mode 100644
index 000000000..0b8005299
--- /dev/null
+++ b/kernel/sound/soc/codecs/gtm601.c
@@ -0,0 +1,95 @@
+/*
+ * This is a simple driver for the GTM601 Voice PCM interface
+ *
+ * Copyright (C) 2015 Goldelico GmbH
+ *
+ * Author: Marek Belisko <marek@goldelico.com>
+ *
+ * Based on wm8727.c driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget gtm601_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("AOUT"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route gtm601_dapm_routes[] = {
+ { "AOUT", NULL, "Playback" },
+ { "Capture", NULL, "AIN" },
+};
+
+static struct snd_soc_dai_driver gtm601_dai = {
+ .name = "gtm601",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static const struct snd_soc_codec_driver soc_codec_dev_gtm601 = {
+ .dapm_widgets = gtm601_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets),
+ .dapm_routes = gtm601_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes),
+};
+
+static int gtm601_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_gtm601, &gtm601_dai, 1);
+}
+
+static int gtm601_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id gtm601_codec_of_match[] = {
+ { .compatible = "option,gtm601", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gtm601_codec_of_match);
+#endif
+
+static struct platform_driver gtm601_codec_driver = {
+ .driver = {
+ .name = "gtm601",
+ .of_match_table = of_match_ptr(gtm601_codec_of_match),
+ },
+ .probe = gtm601_platform_probe,
+ .remove = gtm601_platform_remove,
+};
+
+module_platform_driver(gtm601_codec_driver);
+
+MODULE_DESCRIPTION("ASoC gtm601 driver");
+MODULE_AUTHOR("Marek Belisko <marek@goldelico.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gtm601");
diff --git a/kernel/sound/soc/codecs/hdmi.c b/kernel/sound/soc/codecs/hdmi.c
deleted file mode 100644
index bd42ad34e..000000000
--- a/kernel/sound/soc/codecs/hdmi.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ALSA SoC codec driver for HDMI audio codecs.
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#define DRV_NAME "hdmi-audio-codec"
-
-static const struct snd_soc_dapm_widget hdmi_widgets[] = {
- SND_SOC_DAPM_INPUT("RX"),
- SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route hdmi_routes[] = {
- { "Capture", NULL, "RX" },
- { "TX", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver hdmi_codec_dai = {
- .name = "hdmi-hifi",
- .playback = {
- .stream_name = "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 |
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
- .sig_bits = 24,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 2,
- .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 |
- SNDRV_PCM_FMTBIT_S24_LE,
- },
-
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id hdmi_audio_codec_ids[] = {
- { .compatible = "linux,hdmi-audio", },
- { }
-};
-MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
-#endif
-
-static struct snd_soc_codec_driver hdmi_codec = {
- .dapm_widgets = hdmi_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
- .dapm_routes = hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
- .ignore_pmdown_time = true,
-};
-
-static int hdmi_codec_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
- &hdmi_codec_dai, 1);
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver hdmi_codec_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
- },
-
- .probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
-};
-
-module_platform_driver(hdmi_codec_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/kernel/sound/soc/codecs/ics43432.c b/kernel/sound/soc/codecs/ics43432.c
new file mode 100644
index 000000000..dd850b939
--- /dev/null
+++ b/kernel/sound/soc/codecs/ics43432.c
@@ -0,0 +1,76 @@
+/*
+ * I2S MEMS microphone driver for InvenSense ICS-43432
+ *
+ * - Non configurable.
+ * - I2S interface, 64 BCLs per frame, 32 bits per channel, 24 bit data
+ *
+ * Copyright (c) 2015 Axis Communications AB
+ *
+ * Licensed under GPL v2.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#define ICS43432_RATE_MIN 7190 /* Hz, from data sheet */
+#define ICS43432_RATE_MAX 52800 /* Hz, from data sheet */
+
+#define ICS43432_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
+
+static struct snd_soc_dai_driver ics43432_dai = {
+ .name = "ics43432-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = ICS43432_RATE_MIN,
+ .rate_max = ICS43432_RATE_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = ICS43432_FORMATS,
+ },
+};
+
+static struct snd_soc_codec_driver ics43432_codec_driver = {
+};
+
+static int ics43432_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &ics43432_codec_driver,
+ &ics43432_dai, 1);
+}
+
+static int ics43432_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ics43432_ids[] = {
+ { .compatible = "invensense,ics43432", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ics43432_ids);
+#endif
+
+static struct platform_driver ics43432_driver = {
+ .driver = {
+ .name = "ics43432",
+ .of_match_table = of_match_ptr(ics43432_ids),
+ },
+ .probe = ics43432_probe,
+ .remove = ics43432_remove,
+};
+
+module_platform_driver(ics43432_driver);
+
+MODULE_DESCRIPTION("ASoC ICS43432 driver");
+MODULE_AUTHOR("Ricard Wanderlof <ricardw@axis.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/isabelle.c b/kernel/sound/soc/codecs/isabelle.c
index 3a89ce66d..be448373d 100644
--- a/kernel/sound/soc/codecs/isabelle.c
+++ b/kernel/sound/soc/codecs/isabelle.c
@@ -33,7 +33,7 @@
/* Register default values for ISABELLE driver. */
-static struct reg_default isabelle_reg_defs[] = {
+static const struct reg_default isabelle_reg_defs[] = {
{ 0, 0x00 },
{ 1, 0x00 },
{ 2, 0x00 },
@@ -909,8 +909,6 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1018,25 +1016,25 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops isabelle_hs_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_hs_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
.digital_mute = isabelle_hs_mute,
};
-static struct snd_soc_dai_ops isabelle_hf_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_hf_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
.digital_mute = isabelle_hf_mute,
};
-static struct snd_soc_dai_ops isabelle_line_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_line_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
.digital_mute = isabelle_line_mute,
};
-static struct snd_soc_dai_ops isabelle_ul_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_ul_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
};
@@ -1151,7 +1149,6 @@ MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
static struct i2c_driver isabelle_i2c_driver = {
.driver = {
.name = "isabelle",
- .owner = THIS_MODULE,
},
.probe = isabelle_i2c_probe,
.remove = isabelle_i2c_remove,
diff --git a/kernel/sound/soc/codecs/jz4740.c b/kernel/sound/soc/codecs/jz4740.c
index 933f4476d..1f5ab9995 100644
--- a/kernel/sound/soc/codecs/jz4740.c
+++ b/kernel/sound/soc/codecs/jz4740.c
@@ -78,11 +78,10 @@ struct jz4740_codec {
struct regmap *regmap;
};
-static const unsigned int jz4740_mic_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(jz4740_mic_tlv,
0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
- 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);
@@ -258,7 +257,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 +280,6 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
diff --git a/kernel/sound/soc/codecs/lm4857.c b/kernel/sound/soc/codecs/lm4857.c
index a924bb9d7..558de1053 100644
--- a/kernel/sound/soc/codecs/lm4857.c
+++ b/kernel/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[] = {
@@ -197,10 +142,8 @@ MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "lm4857",
- .owner = THIS_MODULE,
},
.probe = lm4857_i2c_probe,
- .remove = lm4857_i2c_remove,
.id_table = lm4857_i2c_id,
};
diff --git a/kernel/sound/soc/codecs/lm49453.c b/kernel/sound/soc/codecs/lm49453.c
index c4dfde9bd..9af5640e3 100644
--- a/kernel/sound/soc/codecs/lm49453.c
+++ b/kernel/sound/soc/codecs/lm49453.c
@@ -30,7 +30,7 @@
#include <asm/div64.h>
#include "lm49453.h"
-static struct reg_default lm49453_reg_defs[] = {
+static const struct reg_default lm49453_reg_defs[] = {
{ 0, 0x00 },
{ 1, 0x00 },
{ 2, 0x00 },
@@ -188,7 +188,6 @@ static struct reg_default lm49453_reg_defs[] = {
/* codec private data */
struct lm49453_priv {
struct regmap *regmap;
- int fs_rate;
};
/* capture path controls */
@@ -1112,13 +1111,10 @@ static int lm49453_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
u16 clk_div = 0;
- lm49453->fs_rate = params_rate(params);
-
/* Setting DAC clock dividers based on substream sample rate. */
- switch (lm49453->fs_rate) {
+ switch (params_rate(params)) {
case 8000:
case 16000:
case 32000:
@@ -1271,7 +1267,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 +1280,6 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1293,35 +1287,35 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops lm49453_headset_dai_ops = {
+static const struct snd_soc_dai_ops lm49453_headset_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.digital_mute = lm49453_hp_mute,
};
-static struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
+static const struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.digital_mute = lm49453_ls_mute,
};
-static struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
+static const struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.digital_mute = lm49453_ha_mute,
};
-static struct snd_soc_dai_ops lm49453_ep_dai_ops = {
+static const struct snd_soc_dai_ops lm49453_ep_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.digital_mute = lm49453_ep_mute,
};
-static struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
+static const struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
@@ -1462,7 +1456,6 @@ MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
static struct i2c_driver lm49453_i2c_driver = {
.driver = {
.name = "lm49453",
- .owner = THIS_MODULE,
},
.probe = lm49453_i2c_probe,
.remove = lm49453_i2c_remove,
diff --git a/kernel/sound/soc/codecs/max9768.c b/kernel/sound/soc/codecs/max9768.c
index e1c196a41..5b82e26cd 100644
--- a/kernel/sound/soc/codecs/max9768.c
+++ b/kernel/sound/soc/codecs/max9768.c
@@ -35,7 +35,7 @@ struct max9768 {
u32 flags;
};
-static struct reg_default max9768_default_regs[] = {
+static const struct reg_default max9768_default_regs[] = {
{ 0, 0 },
{ 3, MAX9768_CTRL_FILTERLESS},
};
@@ -43,8 +43,8 @@ static struct reg_default max9768_default_regs[] = {
static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
int val = gpio_get_value_cansleep(max9768->mute_gpio);
ucontrol->value.integer.value[0] = !val;
@@ -55,16 +55,15 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
return 0;
}
-static const unsigned int volume_tlv[] = {
- TLV_DB_RANGE_HEAD(43),
+static const DECLARE_TLV_DB_RANGE(volume_tlv,
0, 0, TLV_DB_SCALE_ITEM(-16150, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(-9280, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(-9030, 0, 0),
@@ -107,8 +106,8 @@ static const unsigned int volume_tlv[] = {
51, 57, TLV_DB_SCALE_ITEM(290, 50, 0),
58, 58, TLV_DB_SCALE_ITEM(650, 0, 0),
59, 62, TLV_DB_SCALE_ITEM(700, 60, 0),
- 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0),
-};
+ 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0)
+);
static const struct snd_kcontrol_new max9768_volume[] = {
SOC_SINGLE_TLV("Playback Volume", MAX9768_VOL, 0, 63, 0, volume_tlv),
@@ -130,19 +129,20 @@ static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
{ "OUT-", NULL, "IN" },
};
-static int max9768_probe(struct snd_soc_codec *codec)
+static int max9768_probe(struct snd_soc_component *component)
{
- struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(component);
int ret;
if (max9768->flags & MAX9768_FLAG_CLASSIC_PWM) {
- ret = snd_soc_write(codec, MAX9768_CTRL, MAX9768_CTRL_PWM);
+ ret = regmap_write(max9768->regmap, MAX9768_CTRL,
+ MAX9768_CTRL_PWM);
if (ret)
return ret;
}
if (gpio_is_valid(max9768->mute_gpio)) {
- ret = snd_soc_add_codec_controls(codec, max9768_mute,
+ ret = snd_soc_add_component_controls(component, max9768_mute,
ARRAY_SIZE(max9768_mute));
if (ret)
return ret;
@@ -151,7 +151,7 @@ static int max9768_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver max9768_codec_driver = {
+static struct snd_soc_component_driver max9768_component_driver = {
.probe = max9768_probe,
.controls = max9768_volume,
.num_controls = ARRAY_SIZE(max9768_volume),
@@ -183,11 +183,13 @@ static int max9768_i2c_probe(struct i2c_client *client,
if (pdata) {
/* Mute on powerup to avoid clicks */
- err = gpio_request_one(pdata->mute_gpio, GPIOF_INIT_HIGH, "MAX9768 Mute");
+ err = devm_gpio_request_one(&client->dev, pdata->mute_gpio,
+ GPIOF_INIT_HIGH, "MAX9768 Mute");
max9768->mute_gpio = err ?: pdata->mute_gpio;
/* Activate chip by releasing shutdown, enables I2C */
- err = gpio_request_one(pdata->shdn_gpio, GPIOF_INIT_HIGH, "MAX9768 Shutdown");
+ err = devm_gpio_request_one(&client->dev, pdata->shdn_gpio,
+ GPIOF_INIT_HIGH, "MAX9768 Shutdown");
max9768->shdn_gpio = err ?: pdata->shdn_gpio;
max9768->flags = pdata->flags;
@@ -199,38 +201,11 @@ static int max9768_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, max9768);
max9768->regmap = devm_regmap_init_i2c(client, &max9768_i2c_regmap_config);
- if (IS_ERR(max9768->regmap)) {
- err = PTR_ERR(max9768->regmap);
- goto err_gpio_free;
- }
+ if (IS_ERR(max9768->regmap))
+ return PTR_ERR(max9768->regmap);
- err = snd_soc_register_codec(&client->dev, &max9768_codec_driver, NULL, 0);
- if (err)
- goto err_gpio_free;
-
- return 0;
-
- err_gpio_free:
- if (gpio_is_valid(max9768->shdn_gpio))
- gpio_free(max9768->shdn_gpio);
- if (gpio_is_valid(max9768->mute_gpio))
- gpio_free(max9768->mute_gpio);
-
- return err;
-}
-
-static int max9768_i2c_remove(struct i2c_client *client)
-{
- struct max9768 *max9768 = i2c_get_clientdata(client);
-
- snd_soc_unregister_codec(&client->dev);
-
- if (gpio_is_valid(max9768->shdn_gpio))
- gpio_free(max9768->shdn_gpio);
- if (gpio_is_valid(max9768->mute_gpio))
- gpio_free(max9768->mute_gpio);
-
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &max9768_component_driver, NULL, 0);
}
static const struct i2c_device_id max9768_i2c_id[] = {
@@ -242,10 +217,8 @@ MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
static struct i2c_driver max9768_i2c_driver = {
.driver = {
.name = "max9768",
- .owner = THIS_MODULE,
},
.probe = max9768_i2c_probe,
- .remove = max9768_i2c_remove,
.id_table = max9768_i2c_id,
};
module_i2c_driver(max9768_i2c_driver);
diff --git a/kernel/sound/soc/codecs/max98088.c b/kernel/sound/soc/codecs/max98088.c
index 805b3f8cd..20dcc496d 100644
--- a/kernel/sound/soc/codecs/max98088.c
+++ b/kernel/sound/soc/codecs/max98088.c
@@ -258,292 +258,36 @@ static const struct reg_default max98088_reg[] = {
{ 0xc9, 0x00 }, /* C9 DAI2 biquad */
};
-static struct {
- int readable;
- int writable;
- int vol;
-} max98088_access[M98088_REG_CNT] = {
- { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
- { 0xFF, 0x00, 1 }, /* 01 MIC status */
- { 0xFF, 0x00, 1 }, /* 02 jack status */
- { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
- { 0xFF, 0xFF, 0 }, /* 04 */
- { 0xFF, 0xFF, 0 }, /* 05 */
- { 0xFF, 0xFF, 0 }, /* 06 */
- { 0xFF, 0xFF, 0 }, /* 07 */
- { 0xFF, 0xFF, 0 }, /* 08 */
- { 0xFF, 0xFF, 0 }, /* 09 */
- { 0xFF, 0xFF, 0 }, /* 0A */
- { 0xFF, 0xFF, 0 }, /* 0B */
- { 0xFF, 0xFF, 0 }, /* 0C */
- { 0xFF, 0xFF, 0 }, /* 0D */
- { 0xFF, 0xFF, 0 }, /* 0E */
- { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
-
- { 0xFF, 0xFF, 0 }, /* 10 master clock */
- { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
- { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
- { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
- { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
- { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
- { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
- { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
- { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
- { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
- { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
- { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
- { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
- { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
- { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
- { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
-
- { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
- { 0xFF, 0xFF, 0 }, /* 21 data config */
- { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
- { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
- { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
- { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
- { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
- { 0xFF, 0xFF, 0 }, /* 27 HP control */
- { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
- { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
- { 0xFF, 0xFF, 0 }, /* 2A REC control */
- { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
- { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
- { 0xFF, 0xFF, 0 }, /* 2D SPK control */
- { 0xFF, 0xFF, 0 }, /* 2E sidetone */
- { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
-
- { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
- { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
- { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
- { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
- { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
- { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
- { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
- { 0xFF, 0xFF, 0 }, /* 37 INA level */
- { 0xFF, 0xFF, 0 }, /* 38 INB level */
- { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
- { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
- { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
- { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
- { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
- { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
- { 0xFF, 0xFF, 0 }, /* 3F MIC config */
-
- { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
- { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
- { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
- { 0xFF, 0xFF, 0 }, /* 43 ALC */
- { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
- { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
- { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
- { 0xFF, 0xFF, 0 }, /* 47 audio input */
- { 0xFF, 0xFF, 0 }, /* 48 microphone */
- { 0xFF, 0xFF, 0 }, /* 49 level control */
- { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
- { 0xFF, 0xFF, 0 }, /* 4B jack detect */
- { 0xFF, 0xFF, 0 }, /* 4C input enable */
- { 0xFF, 0xFF, 0 }, /* 4D output enable */
- { 0xFF, 0xFF, 0 }, /* 4E bias control */
- { 0xFF, 0xFF, 0 }, /* 4F DAC power */
-
- { 0xFF, 0xFF, 0 }, /* 50 DAC power */
- { 0xFF, 0xFF, 0 }, /* 51 system */
- { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
-
- { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
-
- { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
-
- { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
-
- { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
-
- { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
-
- { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
-
- { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
- { 0x00, 0x00, 0 }, /* CA */
- { 0x00, 0x00, 0 }, /* CB */
- { 0x00, 0x00, 0 }, /* CC */
- { 0x00, 0x00, 0 }, /* CD */
- { 0x00, 0x00, 0 }, /* CE */
- { 0x00, 0x00, 0 }, /* CF */
-
- { 0x00, 0x00, 0 }, /* D0 */
- { 0x00, 0x00, 0 }, /* D1 */
- { 0x00, 0x00, 0 }, /* D2 */
- { 0x00, 0x00, 0 }, /* D3 */
- { 0x00, 0x00, 0 }, /* D4 */
- { 0x00, 0x00, 0 }, /* D5 */
- { 0x00, 0x00, 0 }, /* D6 */
- { 0x00, 0x00, 0 }, /* D7 */
- { 0x00, 0x00, 0 }, /* D8 */
- { 0x00, 0x00, 0 }, /* D9 */
- { 0x00, 0x00, 0 }, /* DA */
- { 0x00, 0x00, 0 }, /* DB */
- { 0x00, 0x00, 0 }, /* DC */
- { 0x00, 0x00, 0 }, /* DD */
- { 0x00, 0x00, 0 }, /* DE */
- { 0x00, 0x00, 0 }, /* DF */
-
- { 0x00, 0x00, 0 }, /* E0 */
- { 0x00, 0x00, 0 }, /* E1 */
- { 0x00, 0x00, 0 }, /* E2 */
- { 0x00, 0x00, 0 }, /* E3 */
- { 0x00, 0x00, 0 }, /* E4 */
- { 0x00, 0x00, 0 }, /* E5 */
- { 0x00, 0x00, 0 }, /* E6 */
- { 0x00, 0x00, 0 }, /* E7 */
- { 0x00, 0x00, 0 }, /* E8 */
- { 0x00, 0x00, 0 }, /* E9 */
- { 0x00, 0x00, 0 }, /* EA */
- { 0x00, 0x00, 0 }, /* EB */
- { 0x00, 0x00, 0 }, /* EC */
- { 0x00, 0x00, 0 }, /* ED */
- { 0x00, 0x00, 0 }, /* EE */
- { 0x00, 0x00, 0 }, /* EF */
-
- { 0x00, 0x00, 0 }, /* F0 */
- { 0x00, 0x00, 0 }, /* F1 */
- { 0x00, 0x00, 0 }, /* F2 */
- { 0x00, 0x00, 0 }, /* F3 */
- { 0x00, 0x00, 0 }, /* F4 */
- { 0x00, 0x00, 0 }, /* F5 */
- { 0x00, 0x00, 0 }, /* F6 */
- { 0x00, 0x00, 0 }, /* F7 */
- { 0x00, 0x00, 0 }, /* F8 */
- { 0x00, 0x00, 0 }, /* F9 */
- { 0x00, 0x00, 0 }, /* FA */
- { 0x00, 0x00, 0 }, /* FB */
- { 0x00, 0x00, 0 }, /* FC */
- { 0x00, 0x00, 0 }, /* FD */
- { 0x00, 0x00, 0 }, /* FE */
- { 0xFF, 0x00, 1 }, /* FF */
-};
-
static bool max98088_readable_register(struct device *dev, unsigned int reg)
{
- return max98088_access[reg].readable;
+ switch (reg) {
+ case M98088_REG_00_IRQ_STATUS ... 0xC9:
+ case M98088_REG_FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98088_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98088_REG_03_BATTERY_VOLTAGE ... 0xC9:
+ return true;
+ default:
+ return false;
+ }
}
static bool max98088_volatile_register(struct device *dev, unsigned int reg)
{
- return max98088_access[reg].vol;
+ switch (reg) {
+ case M98088_REG_00_IRQ_STATUS ... M98088_REG_03_BATTERY_VOLTAGE:
+ case M98088_REG_FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
}
static const struct regmap_config max98088_regmap = {
@@ -551,6 +295,7 @@ static const struct regmap_config max98088_regmap = {
.val_bits = 8,
.readable_reg = max98088_readable_register,
+ .writeable_reg = max98088_writeable_register,
.volatile_reg = max98088_volatile_register,
.max_register = 0xff,
@@ -680,29 +425,26 @@ static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static const unsigned int max98088_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
- 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
- 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+static const DECLARE_TLV_DB_RANGE(max98088_micboost_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
-static const unsigned int max98088_hp_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98088_hp_tlv,
0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
- 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
-static const unsigned int max98088_spk_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98088_spk_tlv,
0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
- 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
static const struct snd_kcontrol_new max98088_snd_controls[] = {
@@ -1571,7 +1313,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 +1326,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;
}
@@ -2012,7 +1753,6 @@ MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
static struct i2c_driver max98088_i2c_driver = {
.driver = {
.name = "max98088",
- .owner = THIS_MODULE,
},
.probe = max98088_i2c_probe,
.remove = max98088_i2c_remove,
diff --git a/kernel/sound/soc/codecs/max98088.h b/kernel/sound/soc/codecs/max98088.h
index be89a4f4a..efa39bf46 100644
--- a/kernel/sound/soc/codecs/max98088.h
+++ b/kernel/sound/soc/codecs/max98088.h
@@ -16,7 +16,7 @@
*/
#define M98088_REG_00_IRQ_STATUS 0x00
#define M98088_REG_01_MIC_STATUS 0x01
-#define M98088_REG_02_JACK_STAUS 0x02
+#define M98088_REG_02_JACK_STATUS 0x02
#define M98088_REG_03_BATTERY_VOLTAGE 0x03
#define M98088_REG_0F_IRQ_ENABLE 0x0F
#define M98088_REG_10_SYS_CLK 0x10
diff --git a/kernel/sound/soc/codecs/max98090.c b/kernel/sound/soc/codecs/max98090.c
index 3e33ef2ac..584aab83e 100644
--- a/kernel/sound/soc/codecs/max98090.c
+++ b/kernel/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 */
@@ -267,75 +267,8 @@ static bool max98090_volatile_register(struct device *dev, unsigned int reg)
static bool max98090_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case M98090_REG_DEVICE_STATUS:
- case M98090_REG_JACK_STATUS:
- case M98090_REG_INTERRUPT_S:
- case M98090_REG_RESERVED:
- case M98090_REG_LINE_INPUT_CONFIG:
- case M98090_REG_LINE_INPUT_LEVEL:
- case M98090_REG_INPUT_MODE:
- case M98090_REG_MIC1_INPUT_LEVEL:
- case M98090_REG_MIC2_INPUT_LEVEL:
- case M98090_REG_MIC_BIAS_VOLTAGE:
- case M98090_REG_DIGITAL_MIC_ENABLE:
- case M98090_REG_DIGITAL_MIC_CONFIG:
- case M98090_REG_LEFT_ADC_MIXER:
- case M98090_REG_RIGHT_ADC_MIXER:
- case M98090_REG_LEFT_ADC_LEVEL:
- case M98090_REG_RIGHT_ADC_LEVEL:
- case M98090_REG_ADC_BIQUAD_LEVEL:
- case M98090_REG_ADC_SIDETONE:
- case M98090_REG_SYSTEM_CLOCK:
- case M98090_REG_CLOCK_MODE:
- case M98090_REG_CLOCK_RATIO_NI_MSB:
- case M98090_REG_CLOCK_RATIO_NI_LSB:
- case M98090_REG_CLOCK_RATIO_MI_MSB:
- case M98090_REG_CLOCK_RATIO_MI_LSB:
- case M98090_REG_MASTER_MODE:
- case M98090_REG_INTERFACE_FORMAT:
- case M98090_REG_TDM_CONTROL:
- case M98090_REG_TDM_FORMAT:
- case M98090_REG_IO_CONFIGURATION:
- case M98090_REG_FILTER_CONFIG:
- case M98090_REG_DAI_PLAYBACK_LEVEL:
- case M98090_REG_DAI_PLAYBACK_LEVEL_EQ:
- case M98090_REG_LEFT_HP_MIXER:
- case M98090_REG_RIGHT_HP_MIXER:
- case M98090_REG_HP_CONTROL:
- case M98090_REG_LEFT_HP_VOLUME:
- case M98090_REG_RIGHT_HP_VOLUME:
- case M98090_REG_LEFT_SPK_MIXER:
- case M98090_REG_RIGHT_SPK_MIXER:
- case M98090_REG_SPK_CONTROL:
- case M98090_REG_LEFT_SPK_VOLUME:
- case M98090_REG_RIGHT_SPK_VOLUME:
- case M98090_REG_DRC_TIMING:
- case M98090_REG_DRC_COMPRESSOR:
- case M98090_REG_DRC_EXPANDER:
- case M98090_REG_DRC_GAIN:
- case M98090_REG_RCV_LOUTL_MIXER:
- case M98090_REG_RCV_LOUTL_CONTROL:
- case M98090_REG_RCV_LOUTL_VOLUME:
- case M98090_REG_LOUTR_MIXER:
- case M98090_REG_LOUTR_CONTROL:
- case M98090_REG_LOUTR_VOLUME:
- case M98090_REG_JACK_DETECT:
- case M98090_REG_INPUT_ENABLE:
- case M98090_REG_OUTPUT_ENABLE:
- case M98090_REG_LEVEL_CONTROL:
- case M98090_REG_DSP_FILTER_ENABLE:
- case M98090_REG_BIAS_CONTROL:
- case M98090_REG_DAC_CONTROL:
- case M98090_REG_ADC_CONTROL:
- case M98090_REG_DEVICE_SHUTDOWN:
- case M98090_REG_EQUALIZER_BASE ... M98090_REG_EQUALIZER_BASE + 0x68:
- case M98090_REG_RECORD_BIQUAD_BASE ... M98090_REG_RECORD_BIQUAD_BASE + 0x0E:
- case M98090_REG_DMIC3_VOLUME:
- case M98090_REG_DMIC4_VOLUME:
- case M98090_REG_DMIC34_BQ_PREATTEN:
- case M98090_REG_RECORD_TDM_SLOT:
- case M98090_REG_SAMPLE_RATE:
- case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E:
+ case M98090_REG_DEVICE_STATUS ... M98090_REG_INTERRUPT_S:
+ case M98090_REG_LINE_INPUT_CONFIG ... 0xD1:
case M98090_REG_REVISION_ID:
return true;
default:
@@ -360,22 +293,20 @@ static int max98090_reset(struct max98090_priv *max98090)
return ret;
}
-static const unsigned int max98090_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(max98090_micboost_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
- 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv,
-600, 600, 0);
-static const unsigned int max98090_line_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(max98090_line_tlv,
0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0),
- 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
-};
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0)
+);
static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0);
static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0);
@@ -391,38 +322,34 @@ static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0);
static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0);
static const DECLARE_TLV_DB_SCALE(max98090_sdg_tlv, 50, 200, 0);
-static const unsigned int max98090_mixout_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(max98090_mixout_tlv,
0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0),
- 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
-};
+ 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0)
+);
-static const unsigned int max98090_hp_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98090_hp_tlv,
0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
- 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
-static const unsigned int max98090_spk_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98090_spk_tlv,
0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0),
5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0),
11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0),
15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0),
- 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0),
-};
+ 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0)
+);
-static const unsigned int max98090_rcv_lout_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98090_rcv_lout_tlv,
0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
- 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -850,6 +777,19 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int max98090_shdn_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ max98090->shdn_pending = true;
+
+ return 0;
+
+}
+
static const char *mic1_mux_text[] = { "IN12", "IN56" };
static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
@@ -1158,9 +1098,11 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
M98090_SDOEN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
- M98090_DIGMICL_SHIFT, 0, NULL, 0),
+ M98090_DIGMICL_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
- M98090_DIGMICR_SHIFT, 0, NULL, 0),
+ M98090_DIGMICR_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
M98090_AHPF_SHIFT, 0, NULL, 0),
@@ -1205,10 +1147,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
&max98090_right_adc_mixer_controls[0],
ARRAY_SIZE(max98090_right_adc_mixer_controls)),
- SND_SOC_DAPM_ADC("ADCL", NULL, M98090_REG_INPUT_ENABLE,
- M98090_ADLEN_SHIFT, 0),
- SND_SOC_DAPM_ADC("ADCR", NULL, M98090_REG_INPUT_ENABLE,
- M98090_ADREN_SHIFT, 0),
+ SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE,
+ M98090_ADLEN_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE,
+ M98090_ADREN_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
SND_SOC_NOPM, 0, 0),
@@ -1500,7 +1444,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 +1742,20 @@ 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 {
+ ret = clk_prepare_enable(max98090->mclk);
+ if (ret)
+ return ret;
}
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 +1772,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 +2134,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 +2212,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)
@@ -2386,7 +2330,7 @@ EXPORT_SYMBOL_GPL(max98090_mic_detect);
#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000
#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
-static struct snd_soc_dai_ops max98090_dai_ops = {
+static const struct snd_soc_dai_ops max98090_dai_ops = {
.set_sysclk = max98090_dai_set_sysclk,
.set_fmt = max98090_dai_set_fmt,
.set_tdm_slot = max98090_set_tdm_slot,
@@ -2422,6 +2366,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 +2452,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);
@@ -2528,9 +2483,26 @@ static int max98090_remove(struct snd_soc_codec *codec)
return 0;
}
+static void max98090_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ if (max98090->shdn_pending) {
+ snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, 0);
+ msleep(40);
+ snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, M98090_SHDNN_MASK);
+ max98090->shdn_pending = false;
+ }
+}
+
static struct snd_soc_codec_driver soc_codec_dev_max98090 = {
.probe = max98090_probe,
.remove = max98090_remove,
+ .seq_notifier = max98090_seq_notifier,
.set_bias_level = max98090_set_bias_level,
};
@@ -2696,7 +2668,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 },
{ }
};
@@ -2706,7 +2678,6 @@ MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
static struct i2c_driver max98090_i2c_driver = {
.driver = {
.name = "max98090",
- .owner = THIS_MODULE,
.pm = &max98090_pm,
.of_match_table = of_match_ptr(max98090_of_match),
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
diff --git a/kernel/sound/soc/codecs/max98090.h b/kernel/sound/soc/codecs/max98090.h
index 21ff743f5..bc610d9a9 100644
--- a/kernel/sound/soc/codecs/max98090.h
+++ b/kernel/sound/soc/codecs/max98090.h
@@ -1543,6 +1543,7 @@ struct max98090_priv {
unsigned int pa2en;
unsigned int sidetone;
bool master;
+ bool shdn_pending;
};
int max98090_mic_detect(struct snd_soc_codec *codec,
diff --git a/kernel/sound/soc/codecs/max98095.c b/kernel/sound/soc/codecs/max98095.c
index 8fba0c3db..1fedac503 100644
--- a/kernel/sound/soc/codecs/max98095.c
+++ b/kernel/sound/soc/codecs/max98095.c
@@ -202,300 +202,36 @@ static const struct reg_default max98095_reg_def[] = {
{ 0xff, 0x00 }, /* FF */
};
-static struct {
- int readable;
- int writable;
-} max98095_access[M98095_REG_CNT] = {
- { 0x00, 0x00 }, /* 00 */
- { 0xFF, 0x00 }, /* 01 */
- { 0xFF, 0x00 }, /* 02 */
- { 0xFF, 0x00 }, /* 03 */
- { 0xFF, 0x00 }, /* 04 */
- { 0xFF, 0x00 }, /* 05 */
- { 0xFF, 0x00 }, /* 06 */
- { 0xFF, 0x00 }, /* 07 */
- { 0xFF, 0x00 }, /* 08 */
- { 0xFF, 0x00 }, /* 09 */
- { 0xFF, 0x00 }, /* 0A */
- { 0xFF, 0x00 }, /* 0B */
- { 0xFF, 0x00 }, /* 0C */
- { 0xFF, 0x00 }, /* 0D */
- { 0xFF, 0x00 }, /* 0E */
- { 0xFF, 0x9F }, /* 0F */
- { 0xFF, 0xFF }, /* 10 */
- { 0xFF, 0xFF }, /* 11 */
- { 0xFF, 0xFF }, /* 12 */
- { 0xFF, 0xFF }, /* 13 */
- { 0xFF, 0xFF }, /* 14 */
- { 0xFF, 0xFF }, /* 15 */
- { 0xFF, 0xFF }, /* 16 */
- { 0xFF, 0xFF }, /* 17 */
- { 0xFF, 0xFF }, /* 18 */
- { 0xFF, 0xFF }, /* 19 */
- { 0xFF, 0xFF }, /* 1A */
- { 0xFF, 0xFF }, /* 1B */
- { 0xFF, 0xFF }, /* 1C */
- { 0xFF, 0xFF }, /* 1D */
- { 0xFF, 0x77 }, /* 1E */
- { 0xFF, 0x77 }, /* 1F */
- { 0xFF, 0x77 }, /* 20 */
- { 0xFF, 0x77 }, /* 21 */
- { 0xFF, 0x77 }, /* 22 */
- { 0xFF, 0x77 }, /* 23 */
- { 0xFF, 0xFF }, /* 24 */
- { 0xFF, 0x7F }, /* 25 */
- { 0xFF, 0x31 }, /* 26 */
- { 0xFF, 0xFF }, /* 27 */
- { 0xFF, 0xFF }, /* 28 */
- { 0xFF, 0xFF }, /* 29 */
- { 0xFF, 0xF7 }, /* 2A */
- { 0xFF, 0x2F }, /* 2B */
- { 0xFF, 0xEF }, /* 2C */
- { 0xFF, 0xFF }, /* 2D */
- { 0xFF, 0xFF }, /* 2E */
- { 0xFF, 0xFF }, /* 2F */
- { 0xFF, 0xFF }, /* 30 */
- { 0xFF, 0xFF }, /* 31 */
- { 0xFF, 0xFF }, /* 32 */
- { 0xFF, 0xFF }, /* 33 */
- { 0xFF, 0xF7 }, /* 34 */
- { 0xFF, 0x2F }, /* 35 */
- { 0xFF, 0xCF }, /* 36 */
- { 0xFF, 0xFF }, /* 37 */
- { 0xFF, 0xFF }, /* 38 */
- { 0xFF, 0xFF }, /* 39 */
- { 0xFF, 0xFF }, /* 3A */
- { 0xFF, 0xFF }, /* 3B */
- { 0xFF, 0xFF }, /* 3C */
- { 0xFF, 0xFF }, /* 3D */
- { 0xFF, 0xF7 }, /* 3E */
- { 0xFF, 0x2F }, /* 3F */
- { 0xFF, 0xCF }, /* 40 */
- { 0xFF, 0xFF }, /* 41 */
- { 0xFF, 0x77 }, /* 42 */
- { 0xFF, 0xFF }, /* 43 */
- { 0xFF, 0xFF }, /* 44 */
- { 0xFF, 0xFF }, /* 45 */
- { 0xFF, 0xFF }, /* 46 */
- { 0xFF, 0xFF }, /* 47 */
- { 0xFF, 0xFF }, /* 48 */
- { 0xFF, 0x0F }, /* 49 */
- { 0xFF, 0xFF }, /* 4A */
- { 0xFF, 0xFF }, /* 4B */
- { 0xFF, 0x3F }, /* 4C */
- { 0xFF, 0x3F }, /* 4D */
- { 0xFF, 0x3F }, /* 4E */
- { 0xFF, 0xFF }, /* 4F */
- { 0xFF, 0x7F }, /* 50 */
- { 0xFF, 0x7F }, /* 51 */
- { 0xFF, 0x0F }, /* 52 */
- { 0xFF, 0x3F }, /* 53 */
- { 0xFF, 0x3F }, /* 54 */
- { 0xFF, 0x3F }, /* 55 */
- { 0xFF, 0xFF }, /* 56 */
- { 0xFF, 0xFF }, /* 57 */
- { 0xFF, 0xBF }, /* 58 */
- { 0xFF, 0x1F }, /* 59 */
- { 0xFF, 0xBF }, /* 5A */
- { 0xFF, 0x1F }, /* 5B */
- { 0xFF, 0xBF }, /* 5C */
- { 0xFF, 0x3F }, /* 5D */
- { 0xFF, 0x3F }, /* 5E */
- { 0xFF, 0x7F }, /* 5F */
- { 0xFF, 0x7F }, /* 60 */
- { 0xFF, 0x47 }, /* 61 */
- { 0xFF, 0x9F }, /* 62 */
- { 0xFF, 0x9F }, /* 63 */
- { 0xFF, 0x9F }, /* 64 */
- { 0xFF, 0x9F }, /* 65 */
- { 0xFF, 0x9F }, /* 66 */
- { 0xFF, 0xBF }, /* 67 */
- { 0xFF, 0xBF }, /* 68 */
- { 0xFF, 0xFF }, /* 69 */
- { 0xFF, 0xFF }, /* 6A */
- { 0xFF, 0x7F }, /* 6B */
- { 0xFF, 0xF7 }, /* 6C */
- { 0xFF, 0xFF }, /* 6D */
- { 0xFF, 0xFF }, /* 6E */
- { 0xFF, 0x1F }, /* 6F */
- { 0xFF, 0xF7 }, /* 70 */
- { 0xFF, 0xFF }, /* 71 */
- { 0xFF, 0xFF }, /* 72 */
- { 0xFF, 0x1F }, /* 73 */
- { 0xFF, 0xF7 }, /* 74 */
- { 0xFF, 0xFF }, /* 75 */
- { 0xFF, 0xFF }, /* 76 */
- { 0xFF, 0x1F }, /* 77 */
- { 0xFF, 0xF7 }, /* 78 */
- { 0xFF, 0xFF }, /* 79 */
- { 0xFF, 0xFF }, /* 7A */
- { 0xFF, 0x1F }, /* 7B */
- { 0xFF, 0xF7 }, /* 7C */
- { 0xFF, 0xFF }, /* 7D */
- { 0xFF, 0xFF }, /* 7E */
- { 0xFF, 0x1F }, /* 7F */
- { 0xFF, 0xF7 }, /* 80 */
- { 0xFF, 0xFF }, /* 81 */
- { 0xFF, 0xFF }, /* 82 */
- { 0xFF, 0x1F }, /* 83 */
- { 0xFF, 0x7F }, /* 84 */
- { 0xFF, 0x0F }, /* 85 */
- { 0xFF, 0xD8 }, /* 86 */
- { 0xFF, 0xFF }, /* 87 */
- { 0xFF, 0xEF }, /* 88 */
- { 0xFF, 0xFE }, /* 89 */
- { 0xFF, 0xFE }, /* 8A */
- { 0xFF, 0xFF }, /* 8B */
- { 0xFF, 0xFF }, /* 8C */
- { 0xFF, 0x3F }, /* 8D */
- { 0xFF, 0xFF }, /* 8E */
- { 0xFF, 0x3F }, /* 8F */
- { 0xFF, 0x8F }, /* 90 */
- { 0xFF, 0xFF }, /* 91 */
- { 0xFF, 0x3F }, /* 92 */
- { 0xFF, 0xFF }, /* 93 */
- { 0xFF, 0xFF }, /* 94 */
- { 0xFF, 0x0F }, /* 95 */
- { 0xFF, 0x3F }, /* 96 */
- { 0xFF, 0x8C }, /* 97 */
- { 0x00, 0x00 }, /* 98 */
- { 0x00, 0x00 }, /* 99 */
- { 0x00, 0x00 }, /* 9A */
- { 0x00, 0x00 }, /* 9B */
- { 0x00, 0x00 }, /* 9C */
- { 0x00, 0x00 }, /* 9D */
- { 0x00, 0x00 }, /* 9E */
- { 0x00, 0x00 }, /* 9F */
- { 0x00, 0x00 }, /* A0 */
- { 0x00, 0x00 }, /* A1 */
- { 0x00, 0x00 }, /* A2 */
- { 0x00, 0x00 }, /* A3 */
- { 0x00, 0x00 }, /* A4 */
- { 0x00, 0x00 }, /* A5 */
- { 0x00, 0x00 }, /* A6 */
- { 0x00, 0x00 }, /* A7 */
- { 0x00, 0x00 }, /* A8 */
- { 0x00, 0x00 }, /* A9 */
- { 0x00, 0x00 }, /* AA */
- { 0x00, 0x00 }, /* AB */
- { 0x00, 0x00 }, /* AC */
- { 0x00, 0x00 }, /* AD */
- { 0x00, 0x00 }, /* AE */
- { 0x00, 0x00 }, /* AF */
- { 0x00, 0x00 }, /* B0 */
- { 0x00, 0x00 }, /* B1 */
- { 0x00, 0x00 }, /* B2 */
- { 0x00, 0x00 }, /* B3 */
- { 0x00, 0x00 }, /* B4 */
- { 0x00, 0x00 }, /* B5 */
- { 0x00, 0x00 }, /* B6 */
- { 0x00, 0x00 }, /* B7 */
- { 0x00, 0x00 }, /* B8 */
- { 0x00, 0x00 }, /* B9 */
- { 0x00, 0x00 }, /* BA */
- { 0x00, 0x00 }, /* BB */
- { 0x00, 0x00 }, /* BC */
- { 0x00, 0x00 }, /* BD */
- { 0x00, 0x00 }, /* BE */
- { 0x00, 0x00 }, /* BF */
- { 0x00, 0x00 }, /* C0 */
- { 0x00, 0x00 }, /* C1 */
- { 0x00, 0x00 }, /* C2 */
- { 0x00, 0x00 }, /* C3 */
- { 0x00, 0x00 }, /* C4 */
- { 0x00, 0x00 }, /* C5 */
- { 0x00, 0x00 }, /* C6 */
- { 0x00, 0x00 }, /* C7 */
- { 0x00, 0x00 }, /* C8 */
- { 0x00, 0x00 }, /* C9 */
- { 0x00, 0x00 }, /* CA */
- { 0x00, 0x00 }, /* CB */
- { 0x00, 0x00 }, /* CC */
- { 0x00, 0x00 }, /* CD */
- { 0x00, 0x00 }, /* CE */
- { 0x00, 0x00 }, /* CF */
- { 0x00, 0x00 }, /* D0 */
- { 0x00, 0x00 }, /* D1 */
- { 0x00, 0x00 }, /* D2 */
- { 0x00, 0x00 }, /* D3 */
- { 0x00, 0x00 }, /* D4 */
- { 0x00, 0x00 }, /* D5 */
- { 0x00, 0x00 }, /* D6 */
- { 0x00, 0x00 }, /* D7 */
- { 0x00, 0x00 }, /* D8 */
- { 0x00, 0x00 }, /* D9 */
- { 0x00, 0x00 }, /* DA */
- { 0x00, 0x00 }, /* DB */
- { 0x00, 0x00 }, /* DC */
- { 0x00, 0x00 }, /* DD */
- { 0x00, 0x00 }, /* DE */
- { 0x00, 0x00 }, /* DF */
- { 0x00, 0x00 }, /* E0 */
- { 0x00, 0x00 }, /* E1 */
- { 0x00, 0x00 }, /* E2 */
- { 0x00, 0x00 }, /* E3 */
- { 0x00, 0x00 }, /* E4 */
- { 0x00, 0x00 }, /* E5 */
- { 0x00, 0x00 }, /* E6 */
- { 0x00, 0x00 }, /* E7 */
- { 0x00, 0x00 }, /* E8 */
- { 0x00, 0x00 }, /* E9 */
- { 0x00, 0x00 }, /* EA */
- { 0x00, 0x00 }, /* EB */
- { 0x00, 0x00 }, /* EC */
- { 0x00, 0x00 }, /* ED */
- { 0x00, 0x00 }, /* EE */
- { 0x00, 0x00 }, /* EF */
- { 0x00, 0x00 }, /* F0 */
- { 0x00, 0x00 }, /* F1 */
- { 0x00, 0x00 }, /* F2 */
- { 0x00, 0x00 }, /* F3 */
- { 0x00, 0x00 }, /* F4 */
- { 0x00, 0x00 }, /* F5 */
- { 0x00, 0x00 }, /* F6 */
- { 0x00, 0x00 }, /* F7 */
- { 0x00, 0x00 }, /* F8 */
- { 0x00, 0x00 }, /* F9 */
- { 0x00, 0x00 }, /* FA */
- { 0x00, 0x00 }, /* FB */
- { 0x00, 0x00 }, /* FC */
- { 0x00, 0x00 }, /* FD */
- { 0x00, 0x00 }, /* FE */
- { 0xFF, 0x00 }, /* FF */
-};
-
static bool max98095_readable(struct device *dev, unsigned int reg)
{
- if (reg >= M98095_REG_CNT)
- return 0;
- return max98095_access[reg].readable != 0;
+ switch (reg) {
+ case M98095_001_HOST_INT_STS ... M98095_097_PWR_SYS:
+ case M98095_0FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
}
-static bool max98095_volatile(struct device *dev, unsigned int reg)
+static bool max98095_writeable(struct device *dev, unsigned int reg)
{
- if (reg > M98095_REG_MAX_CACHED)
- return 1;
-
switch (reg) {
- case M98095_000_HOST_DATA:
- case M98095_001_HOST_INT_STS:
- case M98095_002_HOST_RSP_STS:
- case M98095_003_HOST_CMD_STS:
- case M98095_004_CODEC_STS:
- case M98095_005_DAI1_ALC_STS:
- case M98095_006_DAI2_ALC_STS:
- case M98095_007_JACK_AUTO_STS:
- case M98095_008_JACK_MANUAL_STS:
- case M98095_009_JACK_VBAT_STS:
- case M98095_00A_ACC_ADC_STS:
- case M98095_00B_MIC_NG_AGC_STS:
- case M98095_00C_SPK_L_VOLT_STS:
- case M98095_00D_SPK_R_VOLT_STS:
- case M98095_00E_TEMP_SENSOR_STS:
- return 1;
+ case M98095_00F_HOST_CFG ... M98095_097_PWR_SYS:
+ return true;
+ default:
+ return false;
}
+}
- return 0;
+static bool max98095_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98095_000_HOST_DATA ... M98095_00E_TEMP_SENSOR_STS:
+ case M98095_REG_MAX_CACHED + 1 ... M98095_0FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
}
static const struct regmap_config max98095_regmap = {
@@ -508,6 +244,7 @@ static const struct regmap_config max98095_regmap = {
.cache_type = REGCACHE_RBTREE,
.readable_reg = max98095_readable,
+ .writeable_reg = max98095_writeable,
.volatile_reg = max98095_volatile,
};
@@ -661,48 +398,43 @@ static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static const unsigned int max98095_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(max98095_micboost_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
- 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0);
static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0);
-static const unsigned int max98095_hp_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98095_hp_tlv,
0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
- 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
-static const unsigned int max98095_spk_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(max98095_spk_tlv,
0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0),
11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0),
19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0),
- 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0),
-};
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
-static const unsigned int max98095_rcv_lout_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(max98095_rcv_lout_tlv,
0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
- 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
-static const unsigned int max98095_lin_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(max98095_lin_tlv,
0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0),
3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0),
- 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
-};
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0)
+);
static const struct snd_kcontrol_new max98095_snd_controls[] = {
@@ -1650,16 +1382,20 @@ 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 {
+ ret = clk_prepare_enable(max98095->mclk);
+ if (ret)
+ return ret;
}
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 +1414,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 +1933,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 +1943,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 +2036,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;
@@ -2431,7 +2166,6 @@ MODULE_DEVICE_TABLE(of, max98095_of_match);
static struct i2c_driver max98095_i2c_driver = {
.driver = {
.name = "max98095",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(max98095_of_match),
},
.probe = max98095_i2c_probe,
diff --git a/kernel/sound/soc/codecs/max98357a.c b/kernel/sound/soc/codecs/max98357a.c
index bf3e933ee..f5e3dce26 100644
--- a/kernel/sound/soc/codecs/max98357a.c
+++ b/kernel/sound/soc/codecs/max98357a.c
@@ -31,6 +31,9 @@ static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
{
struct gpio_desc *sdmode = snd_soc_dai_get_drvdata(dai);
+ if (!sdmode)
+ return 0;
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -48,25 +51,21 @@ static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
}
static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("SDMode", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUTPUT("Speaker"),
};
static const struct snd_soc_dapm_route max98357a_dapm_routes[] = {
- {"Speaker", NULL, "SDMode"},
+ {"Speaker", NULL, "HiFi Playback"},
};
static int max98357a_codec_probe(struct snd_soc_codec *codec)
{
struct gpio_desc *sdmode;
- sdmode = devm_gpiod_get(codec->dev, "sdmode");
- if (IS_ERR(sdmode)) {
- dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n",
- __func__, PTR_ERR(sdmode));
+ sdmode = devm_gpiod_get_optional(codec->dev, "sdmode", GPIOD_OUT_LOW);
+ if (IS_ERR(sdmode))
return PTR_ERR(sdmode);
- }
- gpiod_direction_output(sdmode, 0);
+
snd_soc_codec_set_drvdata(codec, sdmode);
return 0;
@@ -80,7 +79,7 @@ static struct snd_soc_codec_driver max98357a_codec_driver = {
.num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes),
};
-static struct snd_soc_dai_ops max98357a_dai_ops = {
+static const struct snd_soc_dai_ops max98357a_dai_ops = {
.trigger = max98357a_daiops_trigger,
};
@@ -105,15 +104,8 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
static int max98357a_platform_probe(struct platform_device *pdev)
{
- int ret;
-
- ret = snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver,
+ return snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver,
&max98357a_dai_driver, 1);
- if (ret)
- dev_err(&pdev->dev, "%s() error registering codec driver: %d\n",
- __func__, ret);
-
- return ret;
}
static int max98357a_platform_remove(struct platform_device *pdev)
diff --git a/kernel/sound/soc/codecs/max9850.c b/kernel/sound/soc/codecs/max9850.c
index 10f8e47ce..c14a79d02 100644
--- a/kernel/sound/soc/codecs/max9850.c
+++ b/kernel/sound/soc/codecs/max9850.c
@@ -67,13 +67,12 @@ static const struct regmap_config max9850_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static const unsigned int max9850_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(max9850_tlv,
0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
- 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0),
-};
+ 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0)
+);
static const struct snd_kcontrol_new max9850_controls[] = {
SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
@@ -252,7 +251,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 +263,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;
}
@@ -353,7 +351,6 @@ MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
static struct i2c_driver max9850_i2c_driver = {
.driver = {
.name = "max9850",
- .owner = THIS_MODULE,
},
.probe = max9850_i2c_probe,
.remove = max9850_i2c_remove,
diff --git a/kernel/sound/soc/codecs/max9877.c b/kernel/sound/soc/codecs/max9877.c
index 29549cdbf..61cc18e35 100644
--- a/kernel/sound/soc/codecs/max9877.c
+++ b/kernel/sound/soc/codecs/max9877.c
@@ -20,9 +20,7 @@
#include "max9877.h"
-static struct regmap *regmap;
-
-static struct reg_default max9877_regs[] = {
+static const struct reg_default max9877_regs[] = {
{ 0, 0x40 },
{ 1, 0x00 },
{ 2, 0x00 },
@@ -30,19 +28,17 @@ static struct reg_default max9877_regs[] = {
{ 4, 0x49 },
};
-static const unsigned int max9877_pgain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(max9877_pgain_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
- 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
-};
+ 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0)
+);
-static const unsigned int max9877_output_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(max9877_output_tlv,
0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
- 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
-};
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0)
+);
static const char *max9877_out_mode[] = {
"INA -> SPK",
@@ -123,7 +119,7 @@ static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
{ "HPR", NULL, "SHDN" },
};
-static const struct snd_soc_codec_driver max9877_codec = {
+static const struct snd_soc_component_driver max9877_component_driver = {
.controls = max9877_controls,
.num_controls = ARRAY_SIZE(max9877_controls),
@@ -145,6 +141,7 @@ static const struct regmap_config max9877_regmap = {
static int max9877_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct regmap *regmap;
int i;
regmap = devm_regmap_init_i2c(client, &max9877_regmap);
@@ -155,14 +152,8 @@ static int max9877_i2c_probe(struct i2c_client *client,
for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
- return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0);
-}
-
-static int max9877_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &max9877_component_driver, NULL, 0);
}
static const struct i2c_device_id max9877_i2c_id[] = {
@@ -174,10 +165,8 @@ MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
static struct i2c_driver max9877_i2c_driver = {
.driver = {
.name = "max9877",
- .owner = THIS_MODULE,
},
.probe = max9877_i2c_probe,
- .remove = max9877_i2c_remove,
.id_table = max9877_i2c_id,
};
diff --git a/kernel/sound/soc/codecs/max98925.c b/kernel/sound/soc/codecs/max98925.c
index aad664225..5990de317 100644
--- a/kernel/sound/soc/codecs/max98925.c
+++ b/kernel/sound/soc/codecs/max98925.c
@@ -271,8 +271,6 @@ static inline int max98925_rate_value(struct snd_soc_codec *codec,
break;
}
}
- dev_dbg(codec->dev, "%s: sample rate is %d, returning %d\n",
- __func__, rate_table[i].rate, *value);
return ret;
}
@@ -432,7 +430,7 @@ static int max98925_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
- switch (snd_pcm_format_width(params_format(params))) {
+ switch (params_width(params)) {
case 16:
regmap_update_bits(max98925->regmap,
MAX98925_FORMAT,
@@ -523,7 +521,6 @@ static int max98925_probe(struct snd_soc_codec *codec)
struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
max98925->codec = codec;
- codec->control_data = max98925->regmap;
regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00);
/* It's not the default but we need to set DAI_DLY */
regmap_write(max98925->regmap,
@@ -639,7 +636,6 @@ MODULE_DEVICE_TABLE(of, max98925_of_match);
static struct i2c_driver max98925_i2c_driver = {
.driver = {
.name = "max98925",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(max98925_of_match),
.pm = NULL,
},
diff --git a/kernel/sound/soc/codecs/mc13783.c b/kernel/sound/soc/codecs/mc13783.c
index 3d44fc50e..3e770cbe7 100644
--- a/kernel/sound/soc/codecs/mc13783.c
+++ b/kernel/sound/soc/codecs/mc13783.c
@@ -650,14 +650,14 @@ static int mc13783_remove(struct snd_soc_codec *codec)
#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
-static struct snd_soc_dai_ops mc13783_ops_dac = {
+static const struct snd_soc_dai_ops mc13783_ops_dac = {
.hw_params = mc13783_pcm_hw_params_dac,
.set_fmt = mc13783_set_fmt_async,
.set_sysclk = mc13783_set_sysclk_dac,
.set_tdm_slot = mc13783_set_tdm_slot_dac,
};
-static struct snd_soc_dai_ops mc13783_ops_codec = {
+static const struct snd_soc_dai_ops mc13783_ops_codec = {
.hw_params = mc13783_pcm_hw_params_codec,
.set_fmt = mc13783_set_fmt_async,
.set_sysclk = mc13783_set_sysclk_codec,
@@ -698,7 +698,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = {
},
};
-static struct snd_soc_dai_ops mc13783_ops_sync = {
+static const struct snd_soc_dai_ops mc13783_ops_sync = {
.hw_params = mc13783_pcm_hw_params_sync,
.set_fmt = mc13783_set_fmt_sync,
.set_sysclk = mc13783_set_sysclk_sync,
diff --git a/kernel/sound/soc/codecs/ml26124.c b/kernel/sound/soc/codecs/ml26124.c
index 711f55039..f561c78b9 100644
--- a/kernel/sound/soc/codecs/ml26124.c
+++ b/kernel/sound/soc/codecs/ml26124.c
@@ -199,7 +199,7 @@ static const struct clk_coeff coeff_div[] = {
{12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
};
-static struct reg_default ml26124_reg[] = {
+static const struct reg_default ml26124_reg[] = {
/* CLOCK control Register */
{0x00, 0x00 }, /* Sampling Rate */
{0x02, 0x00}, /* PLL NL */
@@ -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;
}
@@ -634,7 +597,6 @@ MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
static struct i2c_driver ml26124_i2c_driver = {
.driver = {
.name = "ml26124",
- .owner = THIS_MODULE,
},
.probe = ml26124_i2c_probe,
.remove = ml26124_i2c_remove,
diff --git a/kernel/sound/soc/codecs/nau8825.c b/kernel/sound/soc/codecs/nau8825.c
new file mode 100644
index 000000000..c1b87c580
--- /dev/null
+++ b/kernel/sound/soc/codecs/nau8825.c
@@ -0,0 +1,1340 @@
+/*
+ * Nuvoton NAU8825 audio codec driver
+ *
+ * Copyright 2015 Google Chromium project.
+ * Author: Anatol Pomozov <anatol@chromium.org>
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+
+#include "nau8825.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+struct nau8825_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8825_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8825_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8825_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8825_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+static const struct reg_default nau8825_reg_defaults[] = {
+ { NAU8825_REG_ENA_CTRL, 0x00ff },
+ { NAU8825_REG_CLK_DIVIDER, 0x0050 },
+ { NAU8825_REG_FLL1, 0x0 },
+ { NAU8825_REG_FLL2, 0x3126 },
+ { NAU8825_REG_FLL3, 0x0008 },
+ { NAU8825_REG_FLL4, 0x0010 },
+ { NAU8825_REG_FLL5, 0x0 },
+ { NAU8825_REG_FLL6, 0x6000 },
+ { NAU8825_REG_FLL_VCO_RSV, 0xf13c },
+ { NAU8825_REG_HSD_CTRL, 0x000c },
+ { NAU8825_REG_JACK_DET_CTRL, 0x0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0 },
+ { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8825_REG_SAR_CTRL, 0x0015 },
+ { NAU8825_REG_KEYDET_CTRL, 0x0110 },
+ { NAU8825_REG_VDET_THRESHOLD_1, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_2, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_3, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_4, 0x0 },
+ { NAU8825_REG_GPIO34_CTRL, 0x0 },
+ { NAU8825_REG_GPIO12_CTRL, 0x0 },
+ { NAU8825_REG_TDM_CTRL, 0x0 },
+ { NAU8825_REG_I2S_PCM_CTRL1, 0x000b },
+ { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8825_REG_LEFT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_BIQ_CTRL, 0x0 },
+ { NAU8825_REG_BIQ_COF1, 0x0 },
+ { NAU8825_REG_BIQ_COF2, 0x0 },
+ { NAU8825_REG_BIQ_COF3, 0x0 },
+ { NAU8825_REG_BIQ_COF4, 0x0 },
+ { NAU8825_REG_BIQ_COF5, 0x0 },
+ { NAU8825_REG_BIQ_COF6, 0x0 },
+ { NAU8825_REG_BIQ_COF7, 0x0 },
+ { NAU8825_REG_BIQ_COF8, 0x0 },
+ { NAU8825_REG_BIQ_COF9, 0x0 },
+ { NAU8825_REG_BIQ_COF10, 0x0 },
+ { NAU8825_REG_ADC_RATE, 0x0010 },
+ { NAU8825_REG_DAC_CTRL1, 0x0001 },
+ { NAU8825_REG_DAC_CTRL2, 0x0 },
+ { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+ { NAU8825_REG_MUTE_CTRL, 0x0 },
+ { NAU8825_REG_HSVOL_CTRL, 0x0 },
+ { NAU8825_REG_DACL_CTRL, 0x02cf },
+ { NAU8825_REG_DACR_CTRL, 0x00cf },
+ { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_IMM_MODE_CTRL, 0x0 },
+ { NAU8825_REG_CLASSG_CTRL, 0x0 },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
+ { NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_BIAS_ADJ, 0x0 },
+ { NAU8825_REG_TRIM_SETTINGS, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0 },
+ { NAU8825_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8825_REG_RDAC, 0x0008 },
+ { NAU8825_REG_MIC_BIAS, 0x0006 },
+ { NAU8825_REG_BOOST, 0x0 },
+ { NAU8825_REG_FEPGA, 0x0 },
+ { NAU8825_REG_POWER_UP_CONTROL, 0x0 },
+ { NAU8825_REG_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_ENA_CTRL:
+ case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
+ case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK:
+ case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET:
+ case NAU8825_REG_IRQ_STATUS:
+ case NAU8825_REG_INT_CLR_KEY_STATUS:
+ case NAU8825_REG_IMM_RMS_L:
+ case NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_I2C_DEVICE_ID:
+ case NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
+ case NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char * const nau8825_adc_decimation[] = {
+ "32", "64", "128", "256"
+};
+
+static const struct soc_enum nau8825_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation);
+
+static const char * const nau8825_dac_oversampl[] = {
+ "64", "256", "128", "", "32"
+};
+
+static const struct soc_enum nau8825_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT,
+ ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400);
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 0, 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL,
+ 6, 0, 0x3f, 1, dac_vol_tlv),
+ SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL,
+ 8, 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+
+ SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+};
+
+/* DAC Mux 0x33[9] and 0x34[9] */
+static const char * const nau8825_dac_src[] = {
+ "DACL", "DACR",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacl_enum, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacr_enum, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src);
+
+static const struct snd_kcontrol_new nau8825_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum);
+
+static const struct snd_kcontrol_new nau8825_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
+ 15, 1),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
+
+ SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
+ 0),
+
+ /* ADC for button press detection */
+ SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_ADC_EN_SFT, 0),
+
+ SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
+ SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
+ SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACL_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
+
+ SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
+ nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+ NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+ NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+ NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+ NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+ NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+ NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+ {"Frontend PGA", NULL, "MIC"},
+ {"ADC", NULL, "Frontend PGA"},
+ {"ADC", NULL, "ADC Clock"},
+ {"ADC", NULL, "ADC Power"},
+ {"AIFTX", NULL, "ADC"},
+
+ {"DDACL", NULL, "Playback"},
+ {"DDACR", NULL, "Playback"},
+ {"DDACL", NULL, "DDAC Clock"},
+ {"DDACR", NULL, "DDAC Clock"},
+ {"DACL Mux", "DACL", "DDACL"},
+ {"DACL Mux", "DACR", "DDACR"},
+ {"DACR Mux", "DACL", "DDACL"},
+ {"DACR Mux", "DACR", "DDACR"},
+ {"HP amp L", NULL, "DACL Mux"},
+ {"HP amp R", NULL, "DACR Mux"},
+ {"HP amp L", NULL, "HP amp power"},
+ {"HP amp R", NULL, "HP amp power"},
+ {"ADACL", NULL, "HP amp L"},
+ {"ADACR", NULL, "HP amp R"},
+ {"ADACL", NULL, "ADACL Clock"},
+ {"ADACR", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 1", NULL, "ADACL"},
+ {"Output Driver R Stage 1", NULL, "ADACR"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+ {"HPOL", NULL, "Output DACL"},
+ {"HPOR", NULL, "Output DACR"},
+ {"HPOL", NULL, "Charge Pump"},
+ {"HPOR", NULL, "Charge Pump"},
+};
+
+static int nau8825_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 nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8825_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8825_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8825_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8825_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8825_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8825_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8825_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8825_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8825_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8825_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK |
+ NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+ ctrl1_val);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+ .hw_params = nau8825_hw_params,
+ .set_fmt = nau8825_set_dai_fmt,
+};
+
+#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8825_dai = {
+ .name = "nau8825-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .ops = &nau8825_dai_ops,
+};
+
+/**
+ * nau8825_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct regmap *regmap = nau8825->regmap;
+
+ nau8825->jack = jack;
+
+ /* Ground HP Outputs[1:0], needed for headset auto detection
+ * Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
+ */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
+
+
+static bool nau8825_is_jack_inserted(struct regmap *regmap)
+{
+ int status;
+
+ regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
+ return !(status & NAU8825_GPIO2JD1);
+}
+
+static void nau8825_restart_jack_detection(struct regmap *regmap)
+{
+ /* this will restart the entire jack detection process including MIC/GND
+ * switching and create interrupts. We have to go from 0 to 1 and back
+ * to 0 to restart.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, 0);
+}
+
+static void nau8825_eject_jack(struct nau8825 *nau8825)
+{
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ struct regmap *regmap = nau8825->regmap;
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ /* ground HPL/HPR, MICGRND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int nau8825_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+static int nau8825_jack_insert(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ int jack_status_reg, mic_detected;
+ int type = 0;
+
+ regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
+ mic_detected = (jack_status_reg >> 10) & 3;
+
+ switch (mic_detected) {
+ case 0:
+ /* no mic */
+ type = SND_JACK_HEADPHONE;
+ break;
+ case 1:
+ dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 1 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ /* Attach SARADC to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKR2);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ case 2:
+ case 3:
+ dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 2 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ /* Attach SARADC to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKSLV);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ }
+
+ if (type & SND_JACK_HEADPHONE) {
+ /* Unground HPL/R */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
+ }
+
+ return type;
+}
+
+#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8825_interrupt(int irq, void *data)
+{
+ struct nau8825 *nau8825 = (struct nau8825 *)data;
+ struct regmap *regmap = nau8825->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+
+ if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) ==
+ NAU8825_JACK_EJECTION_DETECTED) {
+
+ nau8825_eject_jack(nau8825);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK;
+ } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) {
+ int key_status;
+
+ regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS,
+ &key_status);
+
+ /* upper 8 bits of the register are for short pressed keys,
+ * lower 8 bits - for long pressed buttons
+ */
+ nau8825->button_pressed = nau8825_button_decode(
+ key_status >> 8);
+
+ event |= nau8825->button_pressed;
+ event_mask |= NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) {
+ event_mask = NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
+ if (nau8825_is_jack_inserted(regmap)) {
+ event |= nau8825_jack_insert(nau8825);
+ } else {
+ dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
+ nau8825_eject_jack(nau8825);
+ }
+
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8825->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static void nau8825_setup_buttons(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_TRACKING_GAIN_MASK,
+ nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_COMPARE_TIME_MASK,
+ nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_SAMPLING_TIME_MASK,
+ nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_LEVELS_NR_MASK,
+ (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_HYSTERESIS_MASK,
+ nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK,
+ nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1,
+ (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2,
+ (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3,
+ (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4,
+ (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]);
+
+ /* Enable short press and release interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN,
+ 0);
+}
+
+static void nau8825_init_regs(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Enable Bias/Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN);
+
+ /* VMID Tieoff */
+ regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID_SEL_MASK,
+ nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8825_REG_BOOST,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+ NAU8825_SHORT_SHUTDOWN_EN,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+ NAU8825_SHORT_SHUTDOWN_EN);
+
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_OUTPUT_EN,
+ nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_EN,
+ nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_UP,
+ nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY);
+
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_INSERT_DEBOUNCE_MASK,
+ nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_EJECT_DEBOUNCE_MASK,
+ nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT);
+
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff);
+
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+
+ if (nau8825->sar_threshold_num)
+ nau8825_setup_buttons(nau8825);
+
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
+ NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+}
+
+static const struct regmap_config nau8825_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 16,
+
+ .max_register = NAU8825_REG_MAX,
+ .readable_reg = nau8825_readable_reg,
+ .writeable_reg = nau8825_writeable_reg,
+ .volatile_reg = nau8825_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8825_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
+};
+
+static int nau8825_codec_probe(struct snd_soc_codec *codec)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ nau8825->dapm = dapm;
+
+ /* The interrupt clock is gated by x1[10:8],
+ * one of them needs to be enabled all the time for
+ * interrupts to happen.
+ */
+ snd_soc_dapm_force_enable_pin(dapm, "DDACR");
+ snd_soc_dapm_sync(dapm);
+
+ /* Unmask interruptions. Handler uses dapm object so we can enable
+ * interruptions only after dapm is fully initialized.
+ */
+ regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+ nau8825_restart_jack_detection(nau8825->regmap);
+
+ return 0;
+}
+
+/**
+ * nau8825_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
+ struct nau8825_fll *fll_param)
+{
+ u64 fvco;
+ unsigned int fref, i;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(mclk_src_scaling))
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[i].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8825_fll_apply(struct nau8825 *nau8825,
+ struct nau8825_fll *fll_param)
+{
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
+ NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+ /* select divided VCO input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_FILTER_SW_MASK, 0x0000);
+ /* FLL sigma delta modulator enable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8825_fll_apply(nau8825, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ return 0;
+}
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
+ unsigned int freq)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ switch (clk_id) {
+ case NAU8825_CLK_MCLK:
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+
+ /* We selected MCLK source but the clock itself managed externally */
+ if (!nau8825->mclk)
+ break;
+
+ if (!nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ if (nau8825->mclk_freq != freq) {
+ nau8825->mclk_freq = freq;
+
+ freq = clk_round_rate(nau8825->mclk, freq);
+ ret = clk_set_rate(nau8825->mclk, freq);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to set mclk rate\n");
+ return ret;
+ }
+ }
+
+ break;
+ case NAU8825_CLK_INTERNAL:
+ regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+ NAU8825_DCO_EN);
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ default:
+ dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+ return 0;
+}
+
+static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ return nau8825_configure_sysclk(nau8825, clk_id, freq);
+}
+
+static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct nau8825 *nau8825 = 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 (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ ret = regcache_sync(nau8825->regmap);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (nau8825->mclk_freq)
+ clk_disable_unprepare(nau8825->mclk);
+
+ regcache_mark_dirty(nau8825->regmap);
+ break;
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver nau8825_codec_driver = {
+ .probe = nau8825_codec_probe,
+ .set_sysclk = nau8825_set_sysclk,
+ .set_pll = nau8825_set_pll,
+ .set_bias_level = nau8825_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = nau8825_controls,
+ .num_controls = ARRAY_SIZE(nau8825_controls),
+ .dapm_widgets = nau8825_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+ .dapm_routes = nau8825_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+};
+
+static void nau8825_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+}
+
+static void nau8825_print_device_properties(struct nau8825 *nau8825)
+{
+ int i;
+ struct device *dev = nau8825->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num);
+ for (i = 0; i < nau8825->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%d\n", i,
+ nau8825->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8825->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8825->jack_eject_debounce);
+}
+
+static int nau8825_read_device_properties(struct device *dev,
+ struct nau8825 *nau8825) {
+
+ nau8825->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8825->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8825->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8825->jkdet_polarity);
+ device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8825->micbias_voltage);
+ device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8825->vref_impedance);
+ device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8825->sar_threshold_num);
+ device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8825->sar_threshold, nau8825->sar_threshold_num);
+ device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8825->sar_hysteresis);
+ device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8825->sar_voltage);
+ device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8825->sar_compare_time);
+ device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8825->sar_sampling_time);
+ device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8825->key_debounce);
+ device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8825->jack_insert_debounce);
+ device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8825->jack_eject_debounce);
+
+ nau8825->mclk = devm_clk_get(dev, "mclk");
+ if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+ /* The MCLK is managed externally or not used at all */
+ nau8825->mclk = NULL;
+ dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ } else if (IS_ERR(nau8825->mclk)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_setup_irq(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ /* IRQ Output Enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
+
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+
+ /* Enable DDACR needed for interrupts
+ * It is the same as force_enable_pin("DDACR") we do later
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
+
+ /* Chip needs one FSCLK cycle in order to generate interrupts,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+ ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
+ nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8825", nau8825);
+
+ if (ret) {
+ dev_err(nau8825->dev, "Cannot request irq %d (%d)\n",
+ nau8825->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nau8825_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8825) {
+ nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL);
+ if (!nau8825)
+ return -ENOMEM;
+ ret = nau8825_read_device_properties(dev, nau8825);
+ if (ret)
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, nau8825);
+
+ nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config);
+ if (IS_ERR(nau8825->regmap))
+ return PTR_ERR(nau8825->regmap);
+ nau8825->dev = dev;
+ nau8825->irq = i2c->irq;
+
+ nau8825_print_device_properties(nau8825);
+
+ nau8825_reset_chip(nau8825->regmap);
+ ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8825: %d\n",
+ ret);
+ return ret;
+ }
+ if ((value & NAU8825_SOFTWARE_ID_MASK) !=
+ NAU8825_SOFTWARE_ID_NAU8825) {
+ dev_err(dev, "Not a NAU8825 chip\n");
+ return -ENODEV;
+ }
+
+ nau8825_init_regs(nau8825);
+
+ if (i2c->irq)
+ nau8825_setup_irq(nau8825);
+
+ return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver,
+ &nau8825_dai, 1);
+}
+
+static int nau8825_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nau8825_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct nau8825 *nau8825 = dev_get_drvdata(dev);
+
+ disable_irq(client->irq);
+ regcache_cache_only(nau8825->regmap, true);
+ regcache_mark_dirty(nau8825->regmap);
+
+ return 0;
+}
+
+static int nau8825_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct nau8825 *nau8825 = dev_get_drvdata(dev);
+
+ regcache_cache_only(nau8825->regmap, false);
+ regcache_sync(nau8825->regmap);
+ enable_irq(client->irq);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops nau8825_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume)
+};
+
+static const struct i2c_device_id nau8825_i2c_ids[] = {
+ { "nau8825", 0 },
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8825_of_ids[] = {
+ { .compatible = "nuvoton,nau8825", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8825_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8825_acpi_match[] = {
+ { "10508825", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+#endif
+
+static struct i2c_driver nau8825_driver = {
+ .driver = {
+ .name = "nau8825",
+ .of_match_table = of_match_ptr(nau8825_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+ .pm = &nau8825_pm,
+ },
+ .probe = nau8825_i2c_probe,
+ .remove = nau8825_i2c_remove,
+ .id_table = nau8825_i2c_ids,
+};
+module_i2c_driver(nau8825_driver);
+
+MODULE_DESCRIPTION("ASoC nau8825 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/nau8825.h b/kernel/sound/soc/codecs/nau8825.h
new file mode 100644
index 000000000..dff8edb83
--- /dev/null
+++ b/kernel/sound/soc/codecs/nau8825.h
@@ -0,0 +1,341 @@
+/*
+ * NAU8825 ALSA SoC audio driver
+ *
+ * Copyright 2015 Google Inc.
+ * Author: Anatol Pomozov <anatol.pomozov@chrominium.org>
+ *
+ * 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 __NAU8825_H__
+#define __NAU8825_H__
+
+#define NAU8825_REG_RESET 0x00
+#define NAU8825_REG_ENA_CTRL 0x01
+#define NAU8825_REG_CLK_DIVIDER 0x03
+#define NAU8825_REG_FLL1 0x04
+#define NAU8825_REG_FLL2 0x05
+#define NAU8825_REG_FLL3 0x06
+#define NAU8825_REG_FLL4 0x07
+#define NAU8825_REG_FLL5 0x08
+#define NAU8825_REG_FLL6 0x09
+#define NAU8825_REG_FLL_VCO_RSV 0x0a
+#define NAU8825_REG_HSD_CTRL 0x0c
+#define NAU8825_REG_JACK_DET_CTRL 0x0d
+#define NAU8825_REG_INTERRUPT_MASK 0x0f
+#define NAU8825_REG_IRQ_STATUS 0x10
+#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11
+#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12
+#define NAU8825_REG_SAR_CTRL 0x13
+#define NAU8825_REG_KEYDET_CTRL 0x14
+#define NAU8825_REG_VDET_THRESHOLD_1 0x15
+#define NAU8825_REG_VDET_THRESHOLD_2 0x16
+#define NAU8825_REG_VDET_THRESHOLD_3 0x17
+#define NAU8825_REG_VDET_THRESHOLD_4 0x18
+#define NAU8825_REG_GPIO34_CTRL 0x19
+#define NAU8825_REG_GPIO12_CTRL 0x1a
+#define NAU8825_REG_TDM_CTRL 0x1b
+#define NAU8825_REG_I2S_PCM_CTRL1 0x1c
+#define NAU8825_REG_I2S_PCM_CTRL2 0x1d
+#define NAU8825_REG_LEFT_TIME_SLOT 0x1e
+#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f
+#define NAU8825_REG_BIQ_CTRL 0x20
+#define NAU8825_REG_BIQ_COF1 0x21
+#define NAU8825_REG_BIQ_COF2 0x22
+#define NAU8825_REG_BIQ_COF3 0x23
+#define NAU8825_REG_BIQ_COF4 0x24
+#define NAU8825_REG_BIQ_COF5 0x25
+#define NAU8825_REG_BIQ_COF6 0x26
+#define NAU8825_REG_BIQ_COF7 0x27
+#define NAU8825_REG_BIQ_COF8 0x28
+#define NAU8825_REG_BIQ_COF9 0x29
+#define NAU8825_REG_BIQ_COF10 0x2a
+#define NAU8825_REG_ADC_RATE 0x2b
+#define NAU8825_REG_DAC_CTRL1 0x2c
+#define NAU8825_REG_DAC_CTRL2 0x2d
+#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f
+#define NAU8825_REG_ADC_DGAIN_CTRL 0x30
+#define NAU8825_REG_MUTE_CTRL 0x31
+#define NAU8825_REG_HSVOL_CTRL 0x32
+#define NAU8825_REG_DACL_CTRL 0x33
+#define NAU8825_REG_DACR_CTRL 0x34
+#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38
+#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39
+#define NAU8825_REG_ADC_DRC_SLOPES 0x3a
+#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b
+#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45
+#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46
+#define NAU8825_REG_DAC_DRC_SLOPES 0x47
+#define NAU8825_REG_DAC_DRC_ATKDCY 0x48
+#define NAU8825_REG_IMM_MODE_CTRL 0x4c
+#define NAU8825_REG_IMM_RMS_L 0x4d
+#define NAU8825_REG_IMM_RMS_R 0x4e
+#define NAU8825_REG_CLASSG_CTRL 0x50
+#define NAU8825_REG_OPT_EFUSE_CTRL 0x51
+#define NAU8825_REG_MISC_CTRL 0x55
+#define NAU8825_REG_I2C_DEVICE_ID 0x58
+#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
+#define NAU8825_REG_BIAS_ADJ 0x66
+#define NAU8825_REG_TRIM_SETTINGS 0x68
+#define NAU8825_REG_ANALOG_CONTROL_1 0x69
+#define NAU8825_REG_ANALOG_CONTROL_2 0x6a
+#define NAU8825_REG_ANALOG_ADC_1 0x71
+#define NAU8825_REG_ANALOG_ADC_2 0x72
+#define NAU8825_REG_RDAC 0x73
+#define NAU8825_REG_MIC_BIAS 0x74
+#define NAU8825_REG_BOOST 0x76
+#define NAU8825_REG_FEPGA 0x77
+#define NAU8825_REG_POWER_UP_CONTROL 0x7f
+#define NAU8825_REG_CHARGE_PUMP 0x80
+#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8825_REG_GENERAL_STATUS 0x82
+#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
+
+/* ENA_CTRL (0x1) */
+#define NAU8825_ENABLE_DACR_SFT 10
+#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
+#define NAU8825_ENABLE_DACL_SFT 9
+#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_SAR_SFT 1
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8825_CLK_SRC_SFT 15
+#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0)
+
+/* FLL1 (0x04) */
+#define NAU8825_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
+
+/* FLL4 (0x07) */
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
+
+/* FLL5 (0x08) */
+#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
+
+/* FLL6 (0x9) */
+#define NAU8825_DCO_EN_MASK (0x1 << 15)
+#define NAU8825_DCO_EN (0x1 << 15)
+#define NAU8825_DCO_DIS (0x0 << 15)
+#define NAU8825_SDM_EN_MASK (0x1 << 14)
+#define NAU8825_SDM_EN (0x1 << 14)
+#define NAU8825_SDM_DIS (0x0 << 14)
+
+/* HSD_CTRL (0xc) */
+#define NAU8825_HSD_AUTO_MODE (1 << 6)
+/* 0 - short to GND, 1 - open */
+#define NAU8825_SPKR_DWN1R (1 << 1)
+#define NAU8825_SPKR_DWN1L (1 << 0)
+
+/* JACK_DET_CTRL (0xd) */
+#define NAU8825_JACK_DET_RESTART (1 << 9)
+#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0xf) */
+#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
+#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
+#define NAU8825_IRQ_EJECT_EN (1 << 2)
+
+/* IRQ_STATUS (0x10) */
+#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
+#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9)
+#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8)
+#define NAU8825_KEY_IRQ_MASK (0x7 << 5)
+#define NAU8825_KEY_RELEASE_IRQ (1 << 7)
+#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6)
+#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5)
+#define NAU8825_MIC_DETECTION_IRQ (1 << 4)
+#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2)
+#define NAU8825_JACK_EJECTION_DETECTED (1 << 2)
+#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0)
+#define NAU8825_JACK_INSERTION_DETECTED (1 << 0)
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
+#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+
+/* SAR_CTRL (0x13) */
+#define NAU8825_SAR_ADC_EN_SFT 12
+#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT)
+#define NAU8825_SAR_INPUT_MASK (1 << 11)
+#define NAU8825_SAR_INPUT_JKSLV (1 << 11)
+#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
+#define NAU8825_SAR_TRACKING_GAIN_SFT 8
+#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
+#define NAU8825_SAR_COMPARE_TIME_SFT 2
+#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8825_SAR_SAMPLING_TIME_SFT 0
+#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* KEYDET_CTRL (0x14) */
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8825_KEYDET_LEVELS_NR_SFT 8
+#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8825_KEYDET_HYSTERESIS_SFT 0
+#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8825_I2S_BP_SFT 7
+#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_PCMB_SFT 6
+#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_DL_SFT 2
+#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DF_SFT 0
+#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT)
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_MS_SFT 3
+#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8825_ADC_SYNC_DOWN_SFT 0
+#define NAU8825_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8825_ADC_SYNC_DOWN_32 0
+#define NAU8825_ADC_SYNC_DOWN_64 1
+#define NAU8825_ADC_SYNC_DOWN_128 2
+#define NAU8825_ADC_SYNC_DOWN_256 3
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8825_DAC_CLIP_OFF (1 << 7)
+#define NAU8825_DAC_OVERSAMPLE_SFT 0
+#define NAU8825_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8825_DAC_OVERSAMPLE_64 0
+#define NAU8825_DAC_OVERSAMPLE_256 1
+#define NAU8825_DAC_OVERSAMPLE_128 2
+#define NAU8825_DAC_OVERSAMPLE_32 4
+
+/* MUTE_CTRL (0x31) */
+#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
+#define NAU8825_DAC_SOFT_MUTE (1 << 9)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8825_HP_MUTE (1 << 15)
+
+/* DACL_CTRL (0x33) */
+#define NAU8825_DACL_CH_SEL_SFT 9
+
+/* DACR_CTRL (0x34) */
+#define NAU8825_DACR_CH_SEL_SFT 9
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8825_GPIO2JD1 (1 << 7)
+#define NAU8825_SOFTWARE_ID_MASK 0x3
+#define NAU8825_SOFTWARE_ID_NAU8825 0x0
+
+/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_VMID (1 << 6)
+#define NAU8825_BIAS_VMID_SEL_SFT 4
+#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
+#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
+#define NAU8825_DAC_CAPACITOR_LSB (1 << 0)
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8)
+#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8)
+#define NAU8825_ADC_VREFSEL_VMID (1 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
+#define NAU8825_POWERUP_ADCL (1 << 6)
+
+/* MIC_BIAS (0x74) */
+#define NAU8825_MICBIAS_JKSLV (1 << 14)
+#define NAU8825_MICBIAS_JKR2 (1 << 12)
+#define NAU8825_MICBIAS_POWERUP_SFT 8
+#define NAU8825_MICBIAS_VOLTAGE_SFT 0
+#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8825_PRECHARGE_DIS (1 << 13)
+#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_G_DIS (1 << 8)
+#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8825_POWERUP_INTEGR_R (1 << 5)
+#define NAU8825_POWERUP_INTEGR_L (1 << 4)
+#define NAU8825_POWERUP_DRV_IN_R (1 << 3)
+#define NAU8825_POWERUP_DRV_IN_L (1 << 2)
+#define NAU8825_POWERUP_HP_DRV_R (1 << 1)
+#define NAU8825_POWERUP_HP_DRV_L (1 << 0)
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8825_JAMNODCLOW (1 << 10)
+#define NAU8825_POWER_DOWN_DACR (1 << 9)
+#define NAU8825_POWER_DOWN_DACL (1 << 8)
+#define NAU8825_CHANRGE_PUMP_EN (1 << 5)
+
+
+/* System Clock Source */
+enum {
+ NAU8825_CLK_MCLK = 0,
+ NAU8825_CLK_INTERNAL,
+};
+
+struct nau8825 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct clk *mclk;
+ int irq;
+ int mclk_freq; /* 0 - mclk is disabled */
+ int button_pressed;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+};
+
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+
+
+#endif /* __NAU8825_H__ */
diff --git a/kernel/sound/soc/codecs/pcm1681.c b/kernel/sound/soc/codecs/pcm1681.c
index e7ba55797..583252342 100644
--- a/kernel/sound/soc/codecs/pcm1681.c
+++ b/kernel/sound/soc/codecs/pcm1681.c
@@ -95,17 +95,22 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec)
struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
int i = 0, val = -1, enable = 0;
- if (priv->deemph)
- for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++)
- if (pcm1681_deemph[i] == priv->rate)
+ if (priv->deemph) {
+ for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) {
+ if (pcm1681_deemph[i] == priv->rate) {
val = i;
+ break;
+ }
+ }
+ }
if (val != -1) {
regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
PCM1681_DEEMPH_RATE_MASK, val << 3);
enable = 1;
- } else
+ } else {
enable = 0;
+ }
/* enable/disable deemphasis functionality */
return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
@@ -330,7 +335,6 @@ static int pcm1681_i2c_remove(struct i2c_client *client)
static struct i2c_driver pcm1681_i2c_driver = {
.driver = {
.name = "pcm1681",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pcm1681_dt_ids),
},
.id_table = pcm1681_i2c_id,
diff --git a/kernel/sound/soc/codecs/pcm1792a.c b/kernel/sound/soc/codecs/pcm1792a.c
index 57b0c94a7..08bb4863e 100644
--- a/kernel/sound/soc/codecs/pcm1792a.c
+++ b/kernel/sound/soc/codecs/pcm1792a.c
@@ -257,7 +257,6 @@ MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
static struct spi_driver pcm1792a_codec_driver = {
.driver = {
.name = "pcm1792a",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pcm1792a_of_match),
},
.id_table = pcm1792a_spi_ids,
diff --git a/kernel/sound/soc/codecs/pcm512x-i2c.c b/kernel/sound/soc/codecs/pcm512x-i2c.c
index dcdfac0ff..dbff416e3 100644
--- a/kernel/sound/soc/codecs/pcm512x-i2c.c
+++ b/kernel/sound/soc/codecs/pcm512x-i2c.c
@@ -67,7 +67,6 @@ static struct i2c_driver pcm512x_i2c_driver = {
.id_table = pcm512x_i2c_id,
.driver = {
.name = "pcm512x",
- .owner = THIS_MODULE,
.of_match_table = pcm512x_of_match,
.pm = &pcm512x_pm_ops,
},
diff --git a/kernel/sound/soc/codecs/pcm512x-spi.c b/kernel/sound/soc/codecs/pcm512x-spi.c
index 7b64a9cef..712ed6598 100644
--- a/kernel/sound/soc/codecs/pcm512x-spi.c
+++ b/kernel/sound/soc/codecs/pcm512x-spi.c
@@ -64,7 +64,6 @@ static struct spi_driver pcm512x_spi_driver = {
.id_table = pcm512x_spi_id,
.driver = {
.name = "pcm512x",
- .owner = THIS_MODULE,
.of_match_table = pcm512x_of_match,
.pm = &pcm512x_pm_ops,
},
diff --git a/kernel/sound/soc/codecs/pcm512x.c b/kernel/sound/soc/codecs/pcm512x.c
index e12764d15..047c48953 100644
--- a/kernel/sound/soc/codecs/pcm512x.c
+++ b/kernel/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;
}
@@ -1119,7 +1117,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
params_rate(params),
params_channels(params));
- switch (snd_pcm_format_width(params_format(params))) {
+ switch (params_width(params)) {
case 16:
alen = PCM512x_ALEN_16;
break;
@@ -1134,7 +1132,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
break;
default:
dev_err(codec->dev, "Bad frame size: %d\n",
- snd_pcm_format_width(params_format(params)));
+ params_width(params));
return -EINVAL;
}
diff --git a/kernel/sound/soc/codecs/rl6231.c b/kernel/sound/soc/codecs/rl6231.c
index 56650d6c2..1dc68ab08 100644
--- a/kernel/sound/soc/codecs/rl6231.c
+++ b/kernel/sound/soc/codecs/rl6231.c
@@ -11,38 +11,100 @@
*/
#include <linux/module.h>
+#include <linux/regmap.h>
#include "rl6231.h"
/**
- * rl6231_calc_dmic_clk - Calculate the parameter of dmic.
+ * rl6231_get_pre_div - Return the value of pre divider.
+ *
+ * @map: map for setting.
+ * @reg: register.
+ * @sft: shift.
+ *
+ * Return the value of pre divider from given register value.
+ * Return negative error code for unexpected register value.
+ */
+int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft)
+{
+ int pd, val;
+
+ regmap_read(map, reg, &val);
+
+ val = (val >> sft) & 0x7;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ pd = val + 1;
+ break;
+ case 4:
+ pd = 6;
+ break;
+ case 5:
+ pd = 8;
+ break;
+ case 6:
+ pd = 12;
+ break;
+ case 7:
+ pd = 16;
+ break;
+ default:
+ pd = -EINVAL;
+ break;
+ }
+
+ return pd;
+}
+EXPORT_SYMBOL_GPL(rl6231_get_pre_div);
+
+/**
+ * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
*
* @rate: base clock rate.
*
- * Choose dmic clock between 1MHz and 3MHz.
- * It is better for clock to approximate 3MHz.
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
*/
int rl6231_calc_dmic_clk(int rate)
{
- int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
- int i, red, bound, temp;
+ int div[] = {2, 3, 4, 6, 8, 12};
+ int i;
+
+ if (rate < 1000000 * div[0]) {
+ pr_warn("Base clock rate %d is too low\n", rate);
+ return -EINVAL;
+ }
- red = 3000000 * 12;
for (i = 0; i < ARRAY_SIZE(div); i++) {
- bound = div[i] * 3000000;
- if (rate > bound)
+ if ((div[i] % 3) == 0)
continue;
- temp = bound - rate;
- if (temp < red) {
- red = temp;
- idx = i;
- }
+ /* find divider that gives DMIC frequency below 3.072MHz */
+ if (3072000 * div[i] >= rate)
+ return i;
}
- return idx;
+ pr_warn("Base clock rate %d is too high\n", rate);
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
+struct pll_calc_map {
+ unsigned int pll_in;
+ unsigned int pll_out;
+ int k;
+ int n;
+ int m;
+ bool m_bp;
+};
+
+static const struct pll_calc_map pll_preset_table[] = {
+ {19200000, 24576000, 3, 30, 3, false},
+};
+
/**
* rl6231_pll_calc - Calcualte PLL M/N/K code.
* @freq_in: external clock provided to codec.
@@ -57,7 +119,7 @@ int rl6231_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rl6231_pll_code *pll_code)
{
int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
- int k, red, n_t, pll_out, in_t, out_t;
+ int i, k, red, n_t, pll_out, in_t, out_t;
int n = 0, m = 0, m_t = 0;
int red_t = abs(freq_out - freq_in);
bool bypass = false;
@@ -65,6 +127,18 @@ int rl6231_pll_calc(const unsigned int freq_in,
if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) {
+ if (freq_in == pll_preset_table[i].pll_in &&
+ freq_out == pll_preset_table[i].pll_out) {
+ k = pll_preset_table[i].k;
+ m = pll_preset_table[i].m;
+ n = pll_preset_table[i].n;
+ bypass = pll_preset_table[i].m_bp;
+ pr_debug("Use preset PLL parameter table\n");
+ goto code_find;
+ }
+ }
+
k = 100000000 / freq_out - 2;
if (k > RL6231_PLL_K_MAX)
k = RL6231_PLL_K_MAX;
diff --git a/kernel/sound/soc/codecs/rl6231.h b/kernel/sound/soc/codecs/rl6231.h
index 0f7b057ed..4c77b441f 100644
--- a/kernel/sound/soc/codecs/rl6231.h
+++ b/kernel/sound/soc/codecs/rl6231.h
@@ -30,5 +30,6 @@ int rl6231_calc_dmic_clk(int rate);
int rl6231_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rl6231_pll_code *pll_code);
int rl6231_get_clk_info(int sclk, int rate);
+int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft);
#endif /* __RL6231_H__ */
diff --git a/kernel/sound/soc/codecs/rl6347a.c b/kernel/sound/soc/codecs/rl6347a.c
new file mode 100644
index 000000000..a4b910efb
--- /dev/null
+++ b/kernel/sound/soc/codecs/rl6347a.c
@@ -0,0 +1,111 @@
+/*
+ * 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/i2c.h>
+#include <linux/regmap.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/kernel/sound/soc/codecs/rl6347a.h b/kernel/sound/soc/codecs/rl6347a.h
new file mode 100644
index 000000000..e127919cb
--- /dev/null
+++ b/kernel/sound/soc/codecs/rl6347a.h
@@ -0,0 +1,34 @@
+/*
+ * 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__
+
+#include <sound/hda_verbs.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/kernel/sound/soc/codecs/rt286.c b/kernel/sound/soc/codecs/rt286.c
index 0fcda35a3..af2ed774b 100644
--- a/kernel/sound/soc/codecs/rt286.c
+++ b/kernel/sound/soc/codecs/rt286.c
@@ -29,14 +29,16 @@
#include <sound/jack.h>
#include <linux/workqueue.h>
#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,10 +47,9 @@ 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[] = {
+static const struct reg_default rt286_index_def[] = {
{ 0x01, 0xaaaa },
{ 0x02, 0x8aaa },
{ 0x03, 0x0002 },
@@ -185,94 +186,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 +214,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 +222,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 +233,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 +274,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 +305,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 +313,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 +321,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 +900,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 +927,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1173,8 +1087,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),
@@ -1193,7 +1107,7 @@ static const struct acpi_device_id rt286_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
-static struct dmi_system_id force_combo_jack_table[] = {
+static const struct dmi_system_id force_combo_jack_table[] = {
{
.ident = "Intel Wilson Beach",
.matches = {
@@ -1203,7 +1117,7 @@ static struct dmi_system_id force_combo_jack_table[] = {
{ }
};
-static struct dmi_system_id dmi_dell_dino[] = {
+static const struct dmi_system_id dmi_dell_dino[] = {
{
.ident = "Dell Dino",
.matches = {
@@ -1242,11 +1156,16 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
}
if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) {
dev_err(&i2c->dev,
- "Device with ID register %x is not rt286\n", val);
+ "Device with ID register %#x is not rt286\n", val);
return -ENODEV;
}
- rt286->index_cache = rt286_index_def;
+ rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def,
+ sizeof(rt286_index_def), GFP_KERNEL);
+ if (!rt286->index_cache)
+ return -ENOMEM;
+
+ rt286->index_cache_size = INDEX_CACHE_SIZE;
rt286->i2c = i2c;
i2c_set_clientdata(i2c, rt286);
@@ -1343,7 +1262,6 @@ static int rt286_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt286_i2c_driver = {
.driver = {
.name = "rt286",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
.probe = rt286_i2c_probe,
diff --git a/kernel/sound/soc/codecs/rt298.c b/kernel/sound/soc/codecs/rt298.c
new file mode 100644
index 000000000..b3f795c60
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt298.c
@@ -0,0 +1,1274 @@
+/*
+ * rt298.c -- RT298 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+#include <sound/rt298.h>
+
+#include "rl6347a.h"
+#include "rt298.h"
+
+#define RT298_VENDOR_ID 0x10ec0298
+
+struct rt298_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct rt298_platform_data pdata;
+ struct i2c_client *i2c;
+ struct snd_soc_jack *jack;
+ struct delayed_work jack_detect_work;
+ int sys_clk;
+ int clk_id;
+ int is_hp_in;
+};
+
+static const struct reg_default rt298_index_def[] = {
+ { 0x01, 0xa5a8 },
+ { 0x02, 0x8e95 },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaf67 },
+ { 0x08, 0x200f },
+ { 0x09, 0xd010 },
+ { 0x0a, 0x0100 },
+ { 0x0b, 0x0000 },
+ { 0x0d, 0x2800 },
+ { 0x0f, 0x0022 },
+ { 0x19, 0x0217 },
+ { 0x20, 0x0020 },
+ { 0x33, 0x0208 },
+ { 0x46, 0x0300 },
+ { 0x49, 0x4004 },
+ { 0x4f, 0x50c9 },
+ { 0x50, 0x3000 },
+ { 0x63, 0x1b02 },
+ { 0x67, 0x1111 },
+ { 0x68, 0x1016 },
+ { 0x69, 0x273f },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt298_index_def)
+
+static const struct reg_default rt298_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x0000007f },
+ { 0x0023a000, 0x0000007f },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x000000c3 },
+ { 0x00936000, 0x000000c3 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000097 },
+ { 0x00b37200, 0x00000097 },
+ { 0x00b37300, 0x00000097 },
+ { 0x00c37000, 0x00000000 },
+ { 0x00c37100, 0x00000080 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01439000, 0x00000080 },
+ { 0x0143a000, 0x00000080 },
+ { 0x01470700, 0x00000000 },
+ { 0x01470500, 0x00000400 },
+ { 0x01470c00, 0x00000000 },
+ { 0x01470100, 0x00000000 },
+ { 0x01837000, 0x00000000 },
+ { 0x01870500, 0x00000400 },
+ { 0x02050000, 0x00000000 },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000000 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x01870700, 0x00000020 },
+ { 0x00830000, 0x000000c3 },
+ { 0x00930000, 0x000000c3 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt298_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT298_GET_HP_SENSE:
+ case RT298_GET_MIC1_SENSE:
+ case RT298_PROC_COEF:
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0):
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt298_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT298_GET_HP_SENSE:
+ case RT298_GET_MIC1_SENSE:
+ case RT298_SET_AUDIO_POWER:
+ case RT298_SET_HPO_POWER:
+ case RT298_SET_SPK_POWER:
+ case RT298_SET_DMIC1_POWER:
+ case RT298_SPK_MUX:
+ case RT298_HPO_MUX:
+ case RT298_ADC0_MUX:
+ case RT298_ADC1_MUX:
+ case RT298_SET_MIC1:
+ case RT298_SET_PIN_HPO:
+ case RT298_SET_PIN_SPK:
+ case RT298_SET_PIN_DMIC1:
+ case RT298_SPK_EAPD:
+ case RT298_SET_AMP_GAIN_HPO:
+ case RT298_SET_DMIC2_DEFAULT:
+ case RT298_DACL_GAIN:
+ case RT298_DACR_GAIN:
+ case RT298_ADCL_GAIN:
+ case RT298_ADCR_GAIN:
+ case RT298_MIC_GAIN:
+ case RT298_SPOL_GAIN:
+ case RT298_SPOR_GAIN:
+ case RT298_HPOL_GAIN:
+ case RT298_HPOR_GAIN:
+ case RT298_F_DAC_SWITCH:
+ case RT298_F_RECMIX_SWITCH:
+ case RT298_REC_MIC_SWITCH:
+ case RT298_REC_I2S_SWITCH:
+ case RT298_REC_LINE_SWITCH:
+ case RT298_REC_BEEP_SWITCH:
+ case RT298_DAC_FORMAT:
+ case RT298_ADC_FORMAT:
+ case RT298_COEF_INDEX:
+ case RT298_PROC_COEF:
+ case RT298_SET_AMP_GAIN_ADC_IN1:
+ case RT298_SET_AMP_GAIN_ADC_IN2:
+ case RT298_SET_POWER(RT298_DAC_OUT1):
+ case RT298_SET_POWER(RT298_DAC_OUT2):
+ case RT298_SET_POWER(RT298_ADC_IN1):
+ case RT298_SET_POWER(RT298_ADC_IN2):
+ case RT298_SET_POWER(RT298_DMIC2):
+ case RT298_SET_POWER(RT298_MIC1):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifdef CONFIG_PM
+static void rt298_index_sync(struct snd_soc_codec *codec)
+{
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_write(codec, rt298->index_cache[i].reg,
+ rt298->index_cache[i].def);
+ }
+}
+#endif
+
+static int rt298_support_power_controls[] = {
+ RT298_DAC_OUT1,
+ RT298_DAC_OUT2,
+ RT298_ADC_IN1,
+ RT298_ADC_IN2,
+ RT298_MIC1,
+ RT298_DMIC1,
+ RT298_DMIC2,
+ RT298_SPK_OUT,
+ RT298_HP_OUT,
+};
+#define RT298_POWER_REG_LEN ARRAY_SIZE(rt298_support_power_controls)
+
+static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
+{
+ struct snd_soc_dapm_context *dapm;
+ unsigned int val, buf;
+
+ *hp = false;
+ *mic = false;
+
+ if (!rt298->codec)
+ return -EINVAL;
+
+ dapm = snd_soc_codec_get_dapm(rt298->codec);
+
+ if (rt298->pdata.cbj_en) {
+ regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ if (*hp == rt298->is_hp_in)
+ return -1;
+ rt298->is_hp_in = *hp;
+ if (*hp) {
+ /* power on HV,VERF */
+ regmap_update_bits(rt298->regmap,
+ RT298_DC_GAIN, 0x200, 0x200);
+
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
+ /* power LDO1 */
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
+
+ regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24);
+ msleep(50);
+
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xfcc0, 0xd400);
+ msleep(300);
+ regmap_read(rt298->regmap, RT298_CBJ_CTRL2, &val);
+
+ if (0x0070 == (val & 0x0070)) {
+ *mic = true;
+ } else {
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xfcc0, 0xe400);
+ msleep(300);
+ regmap_read(rt298->regmap,
+ RT298_CBJ_CTRL2, &val);
+ if (0x0070 == (val & 0x0070))
+ *mic = true;
+ else
+ *mic = false;
+ }
+ regmap_update_bits(rt298->regmap,
+ RT298_DC_GAIN, 0x200, 0x0);
+
+ } else {
+ *mic = false;
+ regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20);
+ }
+ } else {
+ regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ regmap_read(rt298->regmap, RT298_GET_MIC1_SENSE, &buf);
+ *mic = buf & 0x80000000;
+ }
+
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
+ if (!*hp)
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
+
+ pr_debug("*hp = %d *mic = %d\n", *hp, *mic);
+
+ return 0;
+}
+
+static void rt298_jack_detect_work(struct work_struct *work)
+{
+ struct rt298_priv *rt298 =
+ container_of(work, struct rt298_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ if (rt298_jack_detect(rt298, &hp, &mic) < 0)
+ return;
+
+ if (hp == true)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic == true)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt298->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ rt298->jack = jack;
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt298->jack, 0,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt298_mic_detect);
+
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt298->clk_id == RT298_SCLK_S_MCLK)
+ return 1;
+ else
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt298_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT298_DACL_GAIN,
+ RT298_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT298_ADCL_GAIN,
+ RT298_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_SINGLE_TLV("AMIC Volume", RT298_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+ SOC_DOUBLE_R("Speaker Playback Switch", RT298_SPOL_GAIN,
+ RT298_SPOR_GAIN, RT298_MUTE_SFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt298_front_mix[] = {
+ SOC_DAPM_SINGLE("DAC Switch", RT298_F_DAC_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIX Switch", RT298_F_RECMIX_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt298_rec_mix[] = {
+ SOC_DAPM_SINGLE("Mic1 Switch", RT298_REC_MIC_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("I2S Switch", RT298_REC_I2S_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Line1 Switch", RT298_REC_LINE_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Beep Switch", RT298_REC_BEEP_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spo_enable_control =
+ SOC_DAPM_SINGLE("Switch", RT298_SET_PIN_SPK,
+ RT298_SET_PIN_SFT, 1, 0);
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOL_GAIN,
+ RT298_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOR_GAIN,
+ RT298_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt298_adc_src[] = {
+ "Mic", "RECMIX", "Dmic"
+};
+
+static const int rt298_adc_values[] = {
+ 0, 4, 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt298_adc0_enum, RT298_ADC0_MUX, RT298_ADC_SEL_SFT,
+ RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values);
+
+static const struct snd_kcontrol_new rt298_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt298_adc0_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt298_adc1_enum, RT298_ADC1_MUX, RT298_ADC_SEL_SFT,
+ RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values);
+
+static const struct snd_kcontrol_new rt298_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt298_adc1_enum);
+
+static const char * const rt298_dac_src[] = {
+ "Front", "Surround"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt298_hpo_enum, RT298_HPO_MUX,
+ 0, rt298_dac_src);
+
+static const struct snd_kcontrol_new rt298_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt298_hpo_enum);
+
+/* SPK-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt298_spo_enum, RT298_SPK_MUX,
+ 0, rt298_dac_src);
+
+static const struct snd_kcontrol_new rt298_spo_mux =
+SOC_DAPM_ENUM("SPO source", rt298_spo_enum);
+
+static int rt298_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec,
+ RT298_SPK_EAPD, RT298_SET_EAPD_HIGH);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec,
+ RT298_SPK_EAPD, RT298_SET_EAPD_LOW);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0x20);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int nid;
+
+ nid = (w->reg >> 20) & 0xff;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+ 0x7080, 0x7000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec,
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+ 0x7080, 0x7080);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec,
+ RT298_A_BIAS_CTRL3, 0xc000, 0x8000);
+ snd_soc_update_bits(codec,
+ RT298_A_BIAS_CTRL2, 0xc000, 0x8000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ RT298_A_BIAS_CTRL3, 0xc000, 0x0000);
+ snd_soc_update_bits(codec,
+ RT298_A_BIAS_CTRL2, 0xc000, 0x0000);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_vref_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec,
+ RT298_CBJ_CTRL1, 0x0400, 0x0000);
+ mdelay(50);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1,
+ 12, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1,
+ 0, 1, rt298_vref_event, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT298_POWER_CTRL2,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VREF1", 1, RT298_POWER_CTRL2,
+ 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LV", 2, RT298_POWER_CTRL1,
+ 13, 1, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("MCLK MODE", RT298_PLL_CTRL1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+ 0, 0, rt298_mic1_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("Beep"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA_E("DMIC1", RT298_SET_POWER(RT298_DMIC1), 0, 1,
+ NULL, 0, rt298_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA("DMIC2", RT298_SET_POWER(RT298_DMIC2), 0, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
+ rt298_rec_mix, ARRAY_SIZE(rt298_rec_mix)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT298_SET_POWER(RT298_ADC_IN1), 0, 1,
+ &rt298_adc0_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT298_SET_POWER(RT298_ADC_IN2), 0, 1,
+ &rt298_adc1_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt298_spo_mux),
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt298_hpo_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT298_SET_PIN_HPO,
+ RT298_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_MIXER("Front", RT298_SET_POWER(RT298_DAC_OUT1), 0, 1,
+ rt298_front_mix, ARRAY_SIZE(rt298_front_mix)),
+ SND_SOC_DAPM_PGA("Surround", RT298_SET_POWER(RT298_DAC_OUT2), 0, 1,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
+ &spo_enable_control, rt298_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+};
+
+static const struct snd_soc_dapm_route rt298_dapm_routes[] = {
+
+ {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+ {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+ {"Front", NULL, "MCLK MODE", is_mclk_mode},
+ {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+ {"HP Power", NULL, "LDO1"},
+ {"HP Power", NULL, "LDO2"},
+ {"HP Power", NULL, "LV"},
+ {"HP Power", NULL, "VREF1"},
+ {"HP Power", NULL, "BG_MBIAS"},
+
+ {"MIC1", NULL, "LDO1"},
+ {"MIC1", NULL, "LDO2"},
+ {"MIC1", NULL, "HV"},
+ {"MIC1", NULL, "LV"},
+ {"MIC1", NULL, "VREF"},
+ {"MIC1", NULL, "VREF1"},
+ {"MIC1", NULL, "BG_MBIAS"},
+ {"MIC1", NULL, "MIC1 Input Buffer"},
+
+ {"SPO", NULL, "LDO1"},
+ {"SPO", NULL, "LDO2"},
+ {"SPO", NULL, "HV"},
+ {"SPO", NULL, "LV"},
+ {"SPO", NULL, "VREF"},
+ {"SPO", NULL, "VREF1"},
+ {"SPO", NULL, "BG_MBIAS"},
+
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+ {"DMIC1", NULL, "DMIC Receiver"},
+ {"DMIC2", NULL, "DMIC Receiver"},
+
+ {"RECMIX", "Beep Switch", "Beep"},
+ {"RECMIX", "Line1 Switch", "LINE1"},
+ {"RECMIX", "Mic1 Switch", "MIC1"},
+
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "RECMIX", "RECMIX"},
+ {"ADC 0 Mux", "Mic", "MIC1"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "RECMIX", "RECMIX"},
+ {"ADC 1 Mux", "Mic", "MIC1"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TX", NULL, "ADC 0"},
+ {"AIF2TX", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RX"},
+ {"DAC 1", NULL, "AIF2RX"},
+
+ {"Front", "DAC Switch", "DAC 0"},
+ {"Front", "RECMIX Switch", "RECMIX"},
+
+ {"Surround", NULL, "DAC 1"},
+
+ {"SPK Mux", "Front", "Front"},
+ {"SPK Mux", "Surround", "Surround"},
+
+ {"HPO Mux", "Front", "Front"},
+ {"HPO Mux", "Surround", "Surround"},
+
+ {"SPO", "Switch", "SPK Mux"},
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"SPOL", NULL, "SPO"},
+ {"SPOR", NULL, "SPO"},
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt298_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0;
+ int d_len_code;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt298->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt298->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt298->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(codec->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ d_len_code = 0;
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x0018, d_len_code << 3);
+ dev_dbg(codec->dev, "format val = 0x%x\n", val);
+
+ snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x407f, val);
+ snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x407f, val);
+
+ return 0;
+}
+
+static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x800, 0x800);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x800, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x300, 0x0);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x300, 0x1 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x300, 0x2 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x300, 0x3 << 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+ snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x8000, 0);
+ snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x8000, 0);
+
+ return 0;
+}
+
+static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+
+ if (RT298_SCLK_S_MCLK == clk_id) {
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL2, 0x0100, 0x0);
+ snd_soc_update_bits(codec,
+ RT298_PLL_CTRL1, 0x20, 0x20);
+ } else {
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL2, 0x0100, 0x0100);
+ snd_soc_update_bits(codec,
+ RT298_PLL_CTRL, 0x4, 0x4);
+ snd_soc_update_bits(codec,
+ RT298_PLL_CTRL1, 0x20, 0x0);
+ }
+
+ switch (freq) {
+ case 19200000:
+ if (RT298_SCLK_S_MCLK == clk_id) {
+ dev_err(codec->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (RT298_SCLK_S_MCLK == clk_id) {
+ dev_err(codec->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL2, 0x8, 0x0);
+ snd_soc_update_bits(codec,
+ RT298_CLK_DIV, 0xfc1e, 0x0004);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL2, 0x8, 0x8);
+ snd_soc_update_bits(codec,
+ RT298_CLK_DIV, 0xfc1e, 0x5406);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt298->sys_clk = freq;
+ rt298->clk_id = clk_id;
+
+ return 0;
+}
+
+static int rt298_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ if (50 == ratio)
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_update_bits(codec,
+ RT298_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt298_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY ==
+ snd_soc_codec_get_bias_level(codec)) {
+ snd_soc_write(codec,
+ RT298_SET_AUDIO_POWER, AC_PWRST_D0);
+ snd_soc_update_bits(codec, 0x0d, 0x200, 0x200);
+ snd_soc_update_bits(codec, 0x52, 0x80, 0x0);
+ mdelay(20);
+ snd_soc_update_bits(codec, 0x0d, 0x200, 0x0);
+ snd_soc_update_bits(codec, 0x52, 0x80, 0x80);
+ }
+ break;
+
+ case SND_SOC_BIAS_ON:
+ mdelay(30);
+ snd_soc_update_bits(codec,
+ RT298_CBJ_CTRL1, 0x0400, 0x0400);
+
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_write(codec,
+ RT298_SET_AUDIO_POWER, AC_PWRST_D3);
+ snd_soc_update_bits(codec,
+ RT298_CBJ_CTRL1, 0x0400, 0x0000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rt298_irq(int irq, void *data)
+{
+ struct rt298_priv *rt298 = data;
+ bool hp = false;
+ bool mic = false;
+ int ret, status = 0;
+
+ ret = rt298_jack_detect(rt298, &hp, &mic);
+
+ /* Clear IRQ */
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1);
+
+ if (ret == 0) {
+ if (hp == true)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic == true)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt298->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt298->i2c->dev, 300);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rt298_probe(struct snd_soc_codec *codec)
+{
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ rt298->codec = codec;
+
+ if (rt298->i2c->irq) {
+ regmap_update_bits(rt298->regmap,
+ RT298_IRQ_CTRL, 0x2, 0x2);
+
+ INIT_DELAYED_WORK(&rt298->jack_detect_work,
+ rt298_jack_detect_work);
+ schedule_delayed_work(&rt298->jack_detect_work,
+ msecs_to_jiffies(1250));
+ }
+
+ return 0;
+}
+
+static int rt298_remove(struct snd_soc_codec *codec)
+{
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ cancel_delayed_work_sync(&rt298->jack_detect_work);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt298_suspend(struct snd_soc_codec *codec)
+{
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ rt298->is_hp_in = -1;
+ regcache_cache_only(rt298->regmap, true);
+ regcache_mark_dirty(rt298->regmap);
+
+ return 0;
+}
+
+static int rt298_resume(struct snd_soc_codec *codec)
+{
+ struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt298->regmap, false);
+ rt298_index_sync(codec);
+ regcache_sync(rt298->regmap);
+
+ return 0;
+}
+#else
+#define rt298_suspend NULL
+#define rt298_resume NULL
+#endif
+
+#define RT298_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT298_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt298_aif_dai_ops = {
+ .hw_params = rt298_hw_params,
+ .set_fmt = rt298_set_dai_fmt,
+ .set_sysclk = rt298_set_dai_sysclk,
+ .set_bclk_ratio = rt298_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt298_dai[] = {
+ {
+ .name = "rt298-aif1",
+ .id = RT298_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .ops = &rt298_aif_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "rt298-aif2",
+ .id = RT298_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .ops = &rt298_aif_dai_ops,
+ .symmetric_rates = 1,
+ },
+
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt298 = {
+ .probe = rt298_probe,
+ .remove = rt298_remove,
+ .suspend = rt298_suspend,
+ .resume = rt298_resume,
+ .set_bias_level = rt298_set_bias_level,
+ .idle_bias_off = true,
+ .controls = rt298_snd_controls,
+ .num_controls = ARRAY_SIZE(rt298_snd_controls),
+ .dapm_widgets = rt298_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets),
+ .dapm_routes = rt298_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes),
+};
+
+static const struct regmap_config rt298_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x02370100,
+ .volatile_reg = rt298_volatile_register,
+ .readable_reg = rt298_readable_register,
+ .reg_write = rl6347a_hw_write,
+ .reg_read = rl6347a_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt298_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt298_reg),
+};
+
+static const struct i2c_device_id rt298_i2c_id[] = {
+ {"rt298", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt298_i2c_id);
+
+static const struct acpi_device_id rt298_acpi_match[] = {
+ { "INT343A", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt298_acpi_match);
+
+static int rt298_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt298_priv *rt298;
+ struct device *dev = &i2c->dev;
+ const struct acpi_device_id *acpiid;
+ int i, ret;
+
+ rt298 = devm_kzalloc(&i2c->dev, sizeof(*rt298),
+ GFP_KERNEL);
+ if (NULL == rt298)
+ return -ENOMEM;
+
+ rt298->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt298_regmap);
+ if (IS_ERR(rt298->regmap)) {
+ ret = PTR_ERR(rt298->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt298->regmap,
+ RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
+ if (ret != RT298_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt298\n", ret);
+ return -ENODEV;
+ }
+
+ rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def,
+ sizeof(rt298_index_def), GFP_KERNEL);
+ if (!rt298->index_cache)
+ return -ENOMEM;
+
+ rt298->index_cache_size = INDEX_CACHE_SIZE;
+ rt298->i2c = i2c;
+ i2c_set_clientdata(i2c, rt298);
+
+ /* restore codec default */
+ for (i = 0; i < INDEX_CACHE_SIZE; i++)
+ regmap_write(rt298->regmap, rt298->index_cache[i].reg,
+ rt298->index_cache[i].def);
+ for (i = 0; i < ARRAY_SIZE(rt298_reg); i++)
+ regmap_write(rt298->regmap, rt298_reg[i].reg,
+ rt298_reg[i].def);
+
+ if (pdata)
+ rt298->pdata = *pdata;
+
+ /* enable jack combo mode on supported devices */
+ acpiid = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (acpiid) {
+ rt298->pdata = *(struct rt298_platform_data *)
+ acpiid->driver_data;
+ }
+
+ /* VREF Charging */
+ regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80);
+ regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860);
+ /* Vref2 */
+ regmap_update_bits(rt298->regmap, 0x08, 0x20, 0x20);
+
+ regmap_write(rt298->regmap, RT298_SET_AUDIO_POWER, AC_PWRST_D3);
+
+ for (i = 0; i < RT298_POWER_REG_LEN; i++)
+ regmap_write(rt298->regmap,
+ RT298_SET_POWER(rt298_support_power_controls[i]),
+ AC_PWRST_D1);
+
+ if (!rt298->pdata.cbj_en) {
+ regmap_write(rt298->regmap, RT298_CBJ_CTRL2, 0x0000);
+ regmap_write(rt298->regmap, RT298_MIC1_DET_CTRL, 0x0816);
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xf000, 0xb000);
+ } else {
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xf000, 0x5000);
+ }
+
+ mdelay(10);
+
+ if (!rt298->pdata.gpio2_en)
+ regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x40);
+ else
+ regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0);
+
+ mdelay(10);
+
+ regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000);
+ regmap_update_bits(rt298->regmap,
+ RT298_WIND_FILTER_CTRL, 0x0082, 0x0082);
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+ rt298->is_hp_in = -1;
+
+ if (rt298->i2c->irq) {
+ ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to reguest IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt298,
+ rt298_dai, ARRAY_SIZE(rt298_dai));
+
+ return ret;
+}
+
+static int rt298_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt298_priv *rt298 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt298);
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+
+static struct i2c_driver rt298_i2c_driver = {
+ .driver = {
+ .name = "rt298",
+ .acpi_match_table = ACPI_PTR(rt298_acpi_match),
+ },
+ .probe = rt298_i2c_probe,
+ .remove = rt298_i2c_remove,
+ .id_table = rt298_i2c_id,
+};
+
+module_i2c_driver(rt298_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT298 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/rt298.h b/kernel/sound/soc/codecs/rt298.h
new file mode 100644
index 000000000..31da16265
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt298.h
@@ -0,0 +1,206 @@
+/*
+ * rt298.h -- RT298 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT298_H__
+#define __RT298_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT298_AUDIO_FUNCTION_GROUP 0x01
+#define RT298_DAC_OUT1 0x02
+#define RT298_DAC_OUT2 0x03
+#define RT298_DIG_CVT 0x06
+#define RT298_ADC_IN1 0x09
+#define RT298_ADC_IN2 0x08
+#define RT298_MIXER_IN 0x0b
+#define RT298_MIXER_OUT1 0x0c
+#define RT298_MIXER_OUT2 0x0d
+#define RT298_DMIC1 0x12
+#define RT298_DMIC2 0x13
+#define RT298_SPK_OUT 0x14
+#define RT298_MIC1 0x18
+#define RT298_LINE1 0x1a
+#define RT298_BEEP 0x1d
+#define RT298_SPDIF 0x1e
+#define RT298_VENDOR_REGISTERS 0x20
+#define RT298_HP_OUT 0x21
+#define RT298_MIXER_IN1 0x22
+#define RT298_MIXER_IN2 0x23
+
+#define RT298_SET_PIN_SFT 6
+#define RT298_SET_PIN_ENABLE 0x40
+#define RT298_SET_PIN_DISABLE 0
+#define RT298_SET_EAPD_HIGH 0x2
+#define RT298_SET_EAPD_LOW 0
+
+#define RT298_MUTE_SFT 7
+
+/* Verb commands */
+#define RT298_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT298_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT298_SET_AUDIO_POWER RT298_SET_POWER(RT298_AUDIO_FUNCTION_GROUP)
+#define RT298_SET_HPO_POWER RT298_SET_POWER(RT298_HP_OUT)
+#define RT298_SET_SPK_POWER RT298_SET_POWER(RT298_SPK_OUT)
+#define RT298_SET_DMIC1_POWER RT298_SET_POWER(RT298_DMIC1)
+#define RT298_SPK_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_SPK_OUT, 0)
+#define RT298_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_HP_OUT, 0)
+#define RT298_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN1, 0)
+#define RT298_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN2, 0)
+#define RT298_SET_MIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_MIC1, 0)
+#define RT298_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_HP_OUT, 0)
+#define RT298_SET_PIN_SPK\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPK_OUT, 0)
+#define RT298_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_DMIC1, 0)
+#define RT298_SET_PIN_SPDIF\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPDIF, 0)
+#define RT298_SET_PIN_DIG_CVT\
+ VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT298_DIG_CVT, 0)
+#define RT298_SPK_EAPD\
+ VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT298_SPK_OUT, 0)
+#define RT298_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0)
+#define RT298_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0)
+#define RT298_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN2, 0)
+#define RT298_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_HP_OUT, 0)
+#define RT298_GET_MIC1_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_MIC1, 0)
+#define RT298_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_DMIC2, 0)
+#define RT298_SET_SPDIF_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_SPDIF, 0)
+#define RT298_DACL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0xa000)
+#define RT298_DACR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0x9000)
+#define RT298_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x6000)
+#define RT298_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x5000)
+#define RT298_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIC1, 0x7000)
+#define RT298_SPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0xa000)
+#define RT298_SPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0x9000)
+#define RT298_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0xa000)
+#define RT298_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0x9000)
+#define RT298_F_DAC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7000)
+#define RT298_F_RECMIX_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7100)
+#define RT298_REC_MIC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7000)
+#define RT298_REC_I2S_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7100)
+#define RT298_REC_LINE_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7200)
+#define RT298_REC_BEEP_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7300)
+#define RT298_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_DAC_OUT1, 0)
+#define RT298_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_ADC_IN1, 0)
+#define RT298_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0)
+#define RT298_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0)
+
+/* Index registers */
+#define RT298_A_BIAS_CTRL1 0x01
+#define RT298_A_BIAS_CTRL2 0x02
+#define RT298_POWER_CTRL1 0x03
+#define RT298_A_BIAS_CTRL3 0x04
+#define RT298_POWER_CTRL2 0x08
+#define RT298_I2S_CTRL1 0x09
+#define RT298_I2S_CTRL2 0x0a
+#define RT298_CLK_DIV 0x0b
+#define RT298_DC_GAIN 0x0d
+#define RT298_POWER_CTRL3 0x0f
+#define RT298_MIC1_DET_CTRL 0x19
+#define RT298_MISC_CTRL1 0x20
+#define RT298_IRQ_CTRL 0x33
+#define RT298_WIND_FILTER_CTRL 0x46
+#define RT298_PLL_CTRL1 0x49
+#define RT298_CBJ_CTRL1 0x4f
+#define RT298_CBJ_CTRL2 0x50
+#define RT298_PLL_CTRL 0x63
+#define RT298_DEPOP_CTRL1 0x66
+#define RT298_DEPOP_CTRL2 0x67
+#define RT298_DEPOP_CTRL3 0x68
+#define RT298_DEPOP_CTRL4 0x69
+
+/* SPDIF (0x06) */
+#define RT298_SPDIF_SEL_SFT 0
+#define RT298_SPDIF_SEL_PCM0 0
+#define RT298_SPDIF_SEL_PCM1 1
+#define RT298_SPDIF_SEL_SPOUT 2
+#define RT298_SPDIF_SEL_PP 3
+
+/* RECMIX (0x0b) */
+#define RT298_M_REC_BEEP_SFT 0
+#define RT298_M_REC_LINE1_SFT 1
+#define RT298_M_REC_MIC1_SFT 2
+#define RT298_M_REC_I2S_SFT 3
+
+/* Front (0x0c) */
+#define RT298_M_FRONT_DAC_SFT 0
+#define RT298_M_FRONT_REC_SFT 1
+
+/* SPK-OUT (0x14) */
+#define RT298_M_SPK_MUX_SFT 14
+#define RT298_SPK_SEL_MASK 0x1
+#define RT298_SPK_SEL_SFT 0
+#define RT298_SPK_SEL_F 0
+#define RT298_SPK_SEL_S 1
+
+/* HP-OUT (0x21) */
+#define RT298_M_HP_MUX_SFT 14
+#define RT298_HP_SEL_MASK 0x1
+#define RT298_HP_SEL_SFT 0
+#define RT298_HP_SEL_F 0
+#define RT298_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT298_ADC_SEL_MASK 0x7
+#define RT298_ADC_SEL_SFT 0
+#define RT298_ADC_SEL_SURR 0
+#define RT298_ADC_SEL_FRONT 1
+#define RT298_ADC_SEL_DMIC 2
+#define RT298_ADC_SEL_BEEP 4
+#define RT298_ADC_SEL_LINE1 5
+#define RT298_ADC_SEL_I2S 6
+#define RT298_ADC_SEL_MIC1 7
+
+#define RT298_SCLK_S_MCLK 0
+#define RT298_SCLK_S_PLL 1
+
+enum {
+ RT298_AIF1,
+ RT298_AIF2,
+ RT298_AIFS,
+};
+
+int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#endif /* __RT298_H__ */
+
diff --git a/kernel/sound/soc/codecs/rt5631.c b/kernel/sound/soc/codecs/rt5631.c
index 2c10d7772..1be2bab7d 100644
--- a/kernel/sound/soc/codecs/rt5631.c
+++ b/kernel/sound/soc/codecs/rt5631.c
@@ -174,16 +174,15 @@ static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95625, 375, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52}dB */
-static unsigned int mic_bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+static const DECLARE_TLV_DB_RANGE(mic_bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1546,7 +1545,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 +1568,6 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1615,7 +1613,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;
}
@@ -1726,7 +1724,6 @@ static int rt5631_i2c_remove(struct i2c_client *client)
static struct i2c_driver rt5631_i2c_driver = {
.driver = {
.name = "rt5631",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
},
.probe = rt5631_i2c_probe,
diff --git a/kernel/sound/soc/codecs/rt5640.c b/kernel/sound/soc/codecs/rt5640.c
index 06317f7d9..f2beb1aa5 100644
--- a/kernel/sound/soc/codecs/rt5640.c
+++ b/kernel/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_sequence 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 },
@@ -348,16 +347,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
-static unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
/* Interface data select */
static const char * const rt5640_data_select[] = {
@@ -407,11 +405,14 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
175, 0, dac_vol_tlv),
- /* IN1/IN2 Control */
+ /* IN1/IN2/IN3 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2,
RT5640_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4,
RT5640_BST_SFT2, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2,
+ RT5640_BST_SFT2, 8, 0, bst_tlv),
+
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
@@ -460,10 +461,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5640->sysclk);
+ int idx, rate;
+ rate = rt5640->sysclk / rl6231_get_pre_div(rt5640->regmap,
+ RT5640_ADDA_CLK1, RT5640_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@@ -599,6 +601,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
RT5640_M_HP_L_RM_L_SFT, 1, 1),
SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER,
RT5640_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST2_RM_L_SFT, 1, 1),
SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER,
RT5640_M_BST4_RM_L_SFT, 1, 1),
SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
@@ -612,6 +616,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
RT5640_M_HP_R_RM_R_SFT, 1, 1),
SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER,
RT5640_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST2_RM_R_SFT, 1, 1),
SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER,
RT5640_M_BST4_RM_R_SFT, 1, 1),
SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
@@ -1066,6 +1072,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1N"),
SND_SOC_DAPM_INPUT("IN2P"),
SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_INPUT("IN3P"),
+ SND_SOC_DAPM_INPUT("IN3N"),
SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1082,6 +1090,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
RT5640_PWR_BST1_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
RT5640_PWR_BST4_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST2_BIT, 0, NULL, 0),
/* Input Volume */
SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
RT5640_PWR_IN_L_BIT, 0, NULL, 0),
@@ -1311,6 +1321,7 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"IN1P", NULL, "LDO2"},
{"IN2P", NULL, "LDO2"},
+ {"IN3P", NULL, "LDO2"},
{"DMIC L1", NULL, "DMIC1"},
{"DMIC R1", NULL, "DMIC1"},
@@ -1321,18 +1332,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"BST1", NULL, "IN1N"},
{"BST2", NULL, "IN2P"},
{"BST2", NULL, "IN2N"},
+ {"BST3", NULL, "IN3P"},
+ {"BST3", NULL, "IN3N"},
{"INL VOL", NULL, "IN2P"},
{"INR VOL", NULL, "IN2N"},
{"RECMIXL", "HPOL Switch", "HPOL"},
{"RECMIXL", "INL Switch", "INL VOL"},
+ {"RECMIXL", "BST3 Switch", "BST3"},
{"RECMIXL", "BST2 Switch", "BST2"},
{"RECMIXL", "BST1 Switch", "BST1"},
{"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
{"RECMIXR", "HPOR Switch", "HPOR"},
{"RECMIXR", "INR Switch", "INR VOL"},
+ {"RECMIXR", "BST3 Switch", "BST3"},
{"RECMIXR", "BST2 Switch", "BST2"},
{"RECMIXR", "BST1 Switch", "BST1"},
{"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
@@ -1904,7 +1919,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,
@@ -1936,7 +1951,6 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1969,11 +1983,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);
@@ -1985,18 +2000,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;
@@ -2025,7 +2040,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);
@@ -2156,7 +2171,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 },
@@ -2242,7 +2257,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
if (val != RT5640_DEVICE_ID) {
dev_err(&i2c->dev,
- "Device with ID register %x is not rt5640/39\n", val);
+ "Device with ID register %#x is not rt5640/39\n", val);
return -ENODEV;
}
@@ -2261,6 +2276,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
RT5640_IN_DF2, RT5640_IN_DF2);
+ if (rt5640->pdata.in3_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
rt5640->hp_mute = 1;
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
@@ -2277,7 +2296,6 @@ static int rt5640_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt5640_i2c_driver = {
.driver = {
.name = "rt5640",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt5640_acpi_match),
.of_match_table = of_match_ptr(rt5640_of_match),
},
diff --git a/kernel/sound/soc/codecs/rt5645.c b/kernel/sound/soc/codecs/rt5645.c
index 2ee44abd5..b74840b5b 100644
--- a/kernel/sound/soc/codecs/rt5645.c
+++ b/kernel/sound/soc/codecs/rt5645.c
@@ -18,7 +18,10 @@
#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 <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -39,6 +42,8 @@
#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
+#define RT5645_HWEQ_NUM 57
+
static const struct regmap_range_cfg rt5645_ranges[] = {
{
.name = "PR",
@@ -52,7 +57,7 @@ static const struct regmap_range_cfg rt5645_ranges[] = {
},
};
-static const struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x3d, 0x3600},
{RT5645_PR_BASE + 0x1c, 0xfd20},
{RT5645_PR_BASE + 0x20, 0x611f},
@@ -61,7 +66,7 @@ static const struct reg_default init_list[] = {
};
#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
-static const struct reg_default rt5650_init_list[] = {
+static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
};
@@ -221,6 +226,45 @@ static const struct reg_default rt5645_reg[] = {
{ 0xff, 0x6308 },
};
+struct rt5645_eq_param_s {
+ unsigned short reg;
+ unsigned short val;
+};
+
+static const char *const rt5645_supply_names[] = {
+ "avdd",
+ "cpvdd",
+};
+
+struct rt5645_priv {
+ struct snd_soc_codec *codec;
+ struct rt5645_platform_data pdata;
+ struct regmap *regmap;
+ struct i2c_client *i2c;
+ struct gpio_desc *gpiod_hp_det;
+ struct snd_soc_jack *hp_jack;
+ struct snd_soc_jack *mic_jack;
+ struct snd_soc_jack *btn_jack;
+ struct delayed_work jack_detect_work, rcclock_work;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
+ struct rt5645_eq_param_s *eq_param;
+
+ int codec_type;
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5645_AIFS];
+ int bclk[RT5645_AIFS];
+ int master[RT5645_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+ bool en_button_func;
+ bool hp_on;
+};
+
static int rt5645_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, RT5645_RESET, 0);
@@ -347,6 +391,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:
@@ -357,6 +402,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
case RT5645_DEPOP_M1:
case RT5645_DEPOP_M2:
case RT5645_DEPOP_M3:
+ case RT5645_CHARGE_PUMP:
case RT5645_MICBIAS:
case RT5645_A_JD_CTRL1:
case RT5645_VAD_CTRL4:
@@ -415,58 +461,146 @@ 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 */
-static unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */
+static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0)
+);
+
+static int rt5645_hweq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s);
-static const char * const rt5645_tdm_data_swap_select[] = {
- "L/R", "R/L", "L/L", "R/R"
-};
+ return 0;
+}
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
- RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select);
+static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s *eq_param =
+ (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ int i;
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
- RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select);
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg);
+ eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val);
+ }
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
- RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select);
+ return 0;
+}
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum,
- RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select);
+static bool rt5645_validate_hweq(unsigned short reg)
+{
+ if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+ (reg == RT5645_EQ_CTRL2))
+ return true;
-static const char * const rt5645_tdm_adc_data_select[] = {
- "1/2/R", "2/1/R", "R/1/2", "R/2/1"
-};
+ return false;
+}
+
+static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s *eq_param =
+ (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+ eq_param[i].val = be16_to_cpu(eq_param[i].val);
+ }
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum,
- RT5645_TDM_CTRL_1, 8,
- rt5645_tdm_adc_data_select);
+ /* The final setting of the table should be RT5645_EQ_CTRL2 */
+ for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
+ if (eq_param[i].reg == 0)
+ continue;
+ else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+ return 0;
+ else
+ break;
+ }
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (!rt5645_validate_hweq(eq_param[i].reg) &&
+ eq_param[i].reg != 0)
+ return 0;
+ else if (eq_param[i].reg == 0)
+ break;
+ }
+
+ memcpy(rt5645->eq_param, eq_param,
+ RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
+
+ return 0;
+}
+
+#define RT5645_HWEQ(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt5645_hweq_info, \
+ .get = rt5645_hweq_get, \
+ .put = rt5645_hweq_put \
+}
+
+static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
+ msecs_to_jiffies(200));
+
+ return ret;
+}
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
- SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+ SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw,
+ rt5645_spk_put_volsw, out_vol_tlv),
+
+ /* ClassD modulator Speaker Gain Ratio */
+ SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
+ RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_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,13 +615,13 @@ 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,
- RT5645_BST_SFT1, 8, 0, bst_tlv),
+ RT5645_BST_SFT1, 12, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
RT5645_BST_SFT2, 8, 0, bst_tlv),
@@ -499,34 +633,24 @@ 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,
+ SOC_DOUBLE_TLV("ADC Boost Capture Volume", RT5645_ADC_BST_VOL1,
RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0,
adc_bst_tlv),
- SOC_DOUBLE_TLV("STO2 ADC Boost Gain", RT5645_ADC_BST_VOL1,
- RT5645_STO2_ADC_L_BST_SFT, RT5645_STO2_ADC_R_BST_SFT, 3, 0,
+ SOC_DOUBLE_TLV("Mono ADC Boost Capture Volume", RT5645_ADC_BST_VOL2,
+ RT5645_MONO_ADC_L_BST_SFT, RT5645_MONO_ADC_R_BST_SFT, 3, 0,
adc_bst_tlv),
/* 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),
+ RT5645_HWEQ("Speaker HWEQ"),
};
/**
@@ -542,10 +666,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5645->sysclk);
+ int idx, rate;
+ rate = rt5645->sysclk / rl6231_get_pre_div(rt5645->regmap,
+ RT5645_ADDA_CLK1, RT5645_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@@ -616,6 +741,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
+static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (rt5645_validate_hweq(rt5645->eq_param[i].reg))
+ regmap_write(rt5645->regmap, rt5645->eq_param[i].reg,
+ rt5645->eq_param[i].val);
+ else
+ break;
+ }
+
+ return 0;
+}
+
/**
* rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
* @codec: SoC audio codec device.
@@ -729,14 +870,14 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = {
static const struct snd_kcontrol_new rt5645_dac_l_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
RT5645_M_ADCMIX_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
RT5645_M_DAC1_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5645_dac_r_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
RT5645_M_ADCMIX_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
RT5645_M_DAC1_R_SFT, 1, 1),
};
@@ -1093,7 +1234,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 +1245,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 +1503,87 @@ 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_DEPOP_M2, 0x3100);
+ snd_soc_write(codec, RT5645_CHARGE_PUMP,
+ 0x0e06);
+ snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_HP_DCC_INT1, 0x9f01);
+ msleep(20);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ RT5645_HP_CO_MASK, RT5645_HP_CO_EN);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x3e, 0x7400);
+ snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+ msleep(70);
+ rt5645->hp_on = true;
+ } 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 +1598,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;
@@ -1354,14 +1661,20 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ rt5645_enable_hweq(codec);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L);
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
break;
case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
+ snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L, 0);
@@ -1427,6 +1740,27 @@ static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5650_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (rt5645->hp_on) {
+ msleep(100);
+ rt5645->hp_on = false;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER,
RT5645_PWR_LDO2_BIT, 0, NULL, 0),
@@ -1571,20 +1905,16 @@ 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,
- 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_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),
@@ -1715,6 +2045,26 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("PDM1R"),
SND_SOC_DAPM_OUTPUT("SPOL"),
SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_POST("DAPM_POST", rt5650_hp_event),
+};
+
+static const struct snd_soc_dapm_widget rt5645_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac0_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac1_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac2_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac3_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc_in_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc1_in_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc2_in_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc3_in_mux),
};
static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
@@ -1726,6 +2076,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 +2216,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 +2251,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 +2394,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 +2475,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 +2583,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,9 +2849,11 @@ 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) {
+ if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
@@ -2395,12 +2877,17 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
+ if (rt5645->en_button_func &&
+ snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(0));
break;
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 +2897,146 @@ 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 void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
+ bool enable)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- int gpio_state, jack_type = 0;
- unsigned int val;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ if (enable) {
+ snd_soc_dapm_force_enable_pin(dapm, "ADC L power");
+ snd_soc_dapm_force_enable_pin(dapm, "ADC R power");
+ snd_soc_dapm_sync(dapm);
+
+ snd_soc_update_bits(codec,
+ RT5645_INT_IRQ_ST, 0x8, 0x8);
+ 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);
- if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
- dev_err(codec->dev, "invalid gpio\n");
- return -EINVAL;
+ snd_soc_dapm_disable_pin(dapm, "ADC L power");
+ snd_soc_dapm_disable_pin(dapm, "ADC R power");
+ snd_soc_dapm_sync(dapm);
}
- gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
-
- dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
- gpio_state);
+}
- 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);
+static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
- 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);
+
+ /* for jack type detect */
+ snd_soc_dapm_force_enable_pin(dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_sync(dapm);
+ if (!dapm->card->instantiated) {
+ /* Power up necessary bits for JD if dapm is
+ not ready yet */
+ regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1,
+ 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);
+ }
- snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
- RT5645_CBJ_MN_JD, 0);
- snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
+ regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2,
RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
+ RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
+ msleep(100);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2,
+ RT5645_CBJ_MN_JD, 0);
- msleep(400);
- val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
+ msleep(600);
+ 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 {
+ snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_sync(dapm);
+ rt5645->jack_type = SND_JACK_HEADPHONE;
+ }
+ if (rt5645->pdata.jd_invert)
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
+ } else { /* jack out */
+ rt5645->jack_type = 0;
+
+ regmap_update_bits(rt5645->regmap, RT5645_HP_VOL,
+ RT5645_L_MUTE | RT5645_R_MUTE,
+ RT5645_L_MUTE | RT5645_R_MUTE);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2,
+ RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
+ RT5645_CBJ_BST1_EN, 0);
+
+ if (rt5645->en_button_func)
+ rt5645_enable_push_button_irq(codec, false);
- 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);
+ snd_soc_dapm_disable_pin(dapm, "LDO2");
+ snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_sync(dapm);
+ if (rt5645->pdata.jd_invert)
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
}
- 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_button_detect(struct snd_soc_codec *codec)
+{
+ int btn_type, val;
+
+ val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+ pr_debug("val=0x%x\n", val);
+ btn_type = val & 0xfff0;
+ snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val);
+
+ return btn_type;
+}
+
+static irqreturn_t rt5645_irq(int irq, void *data);
+
int rt5645_set_jack_detect(struct snd_soc_codec *codec,
- 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_GEN_CTRL1,
+ RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
+ }
+ rt5645_irq(0, rt5645);
return 0;
}
@@ -2485,8 +3046,114 @@ static void rt5645_jack_detect_work(struct work_struct *work)
{
struct rt5645_priv *rt5645 =
container_of(work, struct rt5645_priv, jack_detect_work.work);
+ int val, btn_type, gpio_state = 0, report = 0;
+
+ if (!rt5645->codec)
+ return;
+
+ 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;
+ 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);
+}
- rt5645_jack_detect(rt5645->codec);
+static void rt5645_rcclock_work(struct work_struct *work)
+{
+ struct rt5645_priv *rt5645 =
+ container_of(work, struct rt5645_priv, rcclock_work.work);
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD);
}
static irqreturn_t rt5645_irq(int irq, void *data)
@@ -2501,37 +3168,42 @@ static irqreturn_t rt5645_irq(int irq, void *data)
static int rt5645_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
rt5645->codec = codec;
switch (rt5645->codec_type) {
case CODEC_TYPE_RT5645:
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
+ rt5645_specific_dapm_widgets,
+ ARRAY_SIZE(rt5645_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
rt5645_specific_dapm_routes,
ARRAY_SIZE(rt5645_specific_dapm_routes));
break;
case CODEC_TYPE_RT5650:
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5650_specific_dapm_widgets,
ARRAY_SIZE(rt5650_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5650_specific_dapm_routes,
ARRAY_SIZE(rt5650_specific_dapm_routes));
break;
}
- 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) {
- 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);
+ if (rt5645->pdata.jd_mode) {
+ snd_soc_dapm_force_enable_pin(dapm, "JD Power");
+ snd_soc_dapm_force_enable_pin(dapm, "LDO2");
+ snd_soc_dapm_sync(dapm);
}
+ rt5645->eq_param = devm_kzalloc(codec->dev,
+ RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+
return 0;
}
@@ -2570,7 +3242,7 @@ static int rt5645_resume(struct snd_soc_codec *codec)
#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-static struct snd_soc_dai_ops rt5645_aif_dai_ops = {
+static const struct snd_soc_dai_ops rt5645_aif_dai_ops = {
.hw_params = rt5645_hw_params,
.set_fmt = rt5645_set_dai_fmt,
.set_sysclk = rt5645_set_dai_sysclk,
@@ -2592,7 +3264,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.rates = RT5645_STEREO_RATES,
.formats = RT5645_FORMATS,
},
@@ -2666,12 +3338,120 @@ 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 const struct dmi_system_id dmi_platform_intel_braswell[] = {
+ {
+ .ident = "Intel Strago",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
+ },
+ },
+ {
+ .ident = "Google Celes",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),
+ },
+ },
+ {
+ .ident = "Google Ultima",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
+ },
+ },
+ {
+ .ident = "Google Reks",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
+ },
+ },
+ {
+ .ident = "Google Edgar",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Edgar"),
+ },
+ },
+ {
+ .ident = "Google Wizpig",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Wizpig"),
+ },
+ },
+ {
+ .ident = "Google Terra",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Terra"),
+ },
+ },
+ { }
+};
+
+static struct rt5645_platform_data buddy_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+ .jd_invert = true,
+};
+
+static int buddy_quirk_cb(const struct dmi_system_id *id)
+{
+ rt5645_pdata = &buddy_platform_data;
+
+ return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_broadwell[] = {
+ {
+ .ident = "Chrome Buddy",
+ .callback = buddy_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+ },
+ },
+ { }
+};
+
+
+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)
{
struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5645_priv *rt5645;
- int ret;
+ int ret, i;
unsigned int val;
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
@@ -2684,6 +3464,19 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5645->pdata = *pdata;
+ else if (dmi_check_system(dmi_platform_intel_braswell) ||
+ dmi_check_system(dmi_platform_intel_broadwell))
+ 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)) {
@@ -2693,6 +3486,24 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
+ rt5645->supplies[i].supply = rt5645_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(rt5645->supplies),
+ rt5645->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5645->supplies),
+ rt5645->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
switch (val) {
@@ -2704,9 +3515,10 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
break;
default:
dev_err(&i2c->dev,
- "Device with ID register %x is not rt5645 or rt5650\n",
+ "Device with ID register %#x is not rt5645 or rt5650\n",
val);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_enable;
}
regmap_write(rt5645->regmap, RT5645_RESET, 0);
@@ -2728,84 +3540,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_GPIO_CTRL1,
+ RT5645_I2S2_DAC_PIN_MASK, RT5645_I2S2_DAC_PIN_GPIO);
+ 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);
- 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_IRQ_CLK_GATE_CTRL,
+ RT5645_IRQ_CLK_GATE_CTRL);
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,
@@ -2838,27 +3642,31 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+ INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
if (rt5645->i2c->irq) {
ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5645", rt5645);
- if (ret)
+ if (ret) {
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ goto err_enable;
+ }
}
- 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 = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
+ rt5645_dai, ARRAY_SIZE(rt5645_dai));
+ if (ret)
+ goto err_irq;
- ret = gpio_direction_input(rt5645->pdata.hp_det_gpio);
- if (ret)
- dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
- }
+ return 0;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
- rt5645_dai, ARRAY_SIZE(rt5645_dai));
+err_irq:
+ if (rt5645->i2c->irq)
+ free_irq(rt5645->i2c->irq, rt5645);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
+ return ret;
}
static int rt5645_i2c_remove(struct i2c_client *i2c)
@@ -2869,23 +3677,36 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
free_irq(i2c->irq, rt5645);
cancel_delayed_work_sync(&rt5645->jack_detect_work);
-
- if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
- gpio_free(rt5645->pdata.hp_det_gpio);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
snd_soc_unregister_codec(&i2c->dev);
+ regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
return 0;
}
+static void rt5645_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c);
+
+ regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+ RT5645_RING2_SLEEVE_GND, RT5645_RING2_SLEEVE_GND);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD,
+ RT5645_CBJ_MN_JD);
+ regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN,
+ 0);
+ msleep(20);
+ regmap_write(rt5645->regmap, RT5645_RESET, 0);
+}
+
static struct i2c_driver rt5645_i2c_driver = {
.driver = {
.name = "rt5645",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
},
.probe = rt5645_i2c_probe,
- .remove = rt5645_i2c_remove,
+ .remove = rt5645_i2c_remove,
+ .shutdown = rt5645_i2c_shutdown,
.id_table = rt5645_i2c_id,
};
module_i2c_driver(rt5645_i2c_driver);
diff --git a/kernel/sound/soc/codecs/rt5645.h b/kernel/sound/soc/codecs/rt5645.h
index db78e9462..205e0715c 100644
--- a/kernel/sound/soc/codecs/rt5645.h
+++ b/kernel/sound/soc/codecs/rt5645.h
@@ -39,8 +39,8 @@
#define RT5645_STO1_ADC_DIG_VOL 0x1c
#define RT5645_MONO_ADC_DIG_VOL 0x1d
#define RT5645_ADC_BST_VOL1 0x1e
-/* Mixer - D-D */
#define RT5645_ADC_BST_VOL2 0x20
+/* Mixer - D-D */
#define RT5645_STO1_ADC_MIXER 0x27
#define RT5645_MONO_ADC_MIXER 0x28
#define RT5645_AD_DA_MIXER 0x29
@@ -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
@@ -314,12 +315,14 @@
#define RT5645_STO1_ADC_R_BST_SFT 12
#define RT5645_STO1_ADC_COMP_MASK (0x3 << 10)
#define RT5645_STO1_ADC_COMP_SFT 10
-#define RT5645_STO2_ADC_L_BST_MASK (0x3 << 8)
-#define RT5645_STO2_ADC_L_BST_SFT 8
-#define RT5645_STO2_ADC_R_BST_MASK (0x3 << 6)
-#define RT5645_STO2_ADC_R_BST_SFT 6
-#define RT5645_STO2_ADC_COMP_MASK (0x3 << 4)
-#define RT5645_STO2_ADC_COMP_SFT 4
+
+/* ADC Boost Volume Control (0x20) */
+#define RT5645_MONO_ADC_L_BST_MASK (0x3 << 14)
+#define RT5645_MONO_ADC_L_BST_SFT 14
+#define RT5645_MONO_ADC_R_BST_MASK (0x3 << 12)
+#define RT5645_MONO_ADC_R_BST_SFT 12
+#define RT5645_MONO_ADC_COMP_MASK (0x3 << 10)
+#define RT5645_MONO_ADC_COMP_SFT 10
/* Stereo2 ADC Mixer Control (0x26) */
#define RT5645_STO2_ADC_SRC_MASK (0x1 << 15)
@@ -618,14 +621,14 @@
#define RT5645_G_OM_L_SM_L_SFT 6
#define RT5645_M_BST1_L_SM_L (0x1 << 5)
#define RT5645_M_BST1_L_SM_L_SFT 5
+#define RT5645_M_BST3_L_SM_L (0x1 << 4)
+#define RT5645_M_BST3_L_SM_L_SFT 4
#define RT5645_M_IN_L_SM_L (0x1 << 3)
#define RT5645_M_IN_L_SM_L_SFT 3
-#define RT5645_M_DAC_L1_SM_L (0x1 << 1)
-#define RT5645_M_DAC_L1_SM_L_SFT 1
#define RT5645_M_DAC_L2_SM_L (0x1 << 2)
#define RT5645_M_DAC_L2_SM_L_SFT 2
-#define RT5645_M_BST3_L_SM_L (0x1 << 4)
-#define RT5645_M_BST3_L_SM_L_SFT 4
+#define RT5645_M_DAC_L1_SM_L (0x1 << 1)
+#define RT5645_M_DAC_L1_SM_L_SFT 1
/* SPK Right Mixer Control (0x47) */
#define RT5645_G_RM_R_SM_R_MASK (0x3 << 14)
@@ -640,14 +643,14 @@
#define RT5645_G_OM_R_SM_R_SFT 6
#define RT5645_M_BST2_R_SM_R (0x1 << 5)
#define RT5645_M_BST2_R_SM_R_SFT 5
+#define RT5645_M_BST3_R_SM_R (0x1 << 4)
+#define RT5645_M_BST3_R_SM_R_SFT 4
#define RT5645_M_IN_R_SM_R (0x1 << 3)
#define RT5645_M_IN_R_SM_R_SFT 3
-#define RT5645_M_DAC_R1_SM_R (0x1 << 1)
-#define RT5645_M_DAC_R1_SM_R_SFT 1
#define RT5645_M_DAC_R2_SM_R (0x1 << 2)
#define RT5645_M_DAC_R2_SM_R_SFT 2
-#define RT5645_M_BST3_R_SM_R (0x1 << 4)
-#define RT5645_M_BST3_R_SM_R_SFT 4
+#define RT5645_M_DAC_R1_SM_R (0x1 << 1)
+#define RT5645_M_DAC_R1_SM_R_SFT 1
/* SPOLMIX Control (0x48) */
#define RT5645_M_DAC_L1_SPM_L (0x1 << 15)
@@ -667,13 +670,17 @@
#define RT5645_M_SV_R_SPM_R (0x1 << 0)
#define RT5645_M_SV_R_SPM_R_SFT 0
+/* SPOMIX Ratio Control (0x4a) */
+#define RT5645_SPK_G_CLSD_MASK (0x7 << 0)
+#define RT5645_SPK_G_CLSD_SFT 0
+
/* Mono Output Mixer Control (0x4c) */
+#define RT5645_G_MONOMIX_MASK (0x1 << 10)
+#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_OV_L_MM (0x1 << 9)
#define RT5645_M_OV_L_MM_SFT 9
#define RT5645_M_DAC_L2_MA (0x1 << 8)
#define RT5645_M_DAC_L2_MA_SFT 8
-#define RT5645_G_MONOMIX_MASK (0x1 << 10)
-#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_BST2_MM (0x1 << 4)
#define RT5645_M_BST2_MM_SFT 4
#define RT5645_M_DAC_R1_MM (0x1 << 3)
@@ -776,8 +783,6 @@
#define RT5645_PWR_CLS_D_R_BIT 9
#define RT5645_PWR_CLS_D_L (0x1 << 8)
#define RT5645_PWR_CLS_D_L_BIT 8
-#define RT5645_PWR_ADC_R (0x1 << 1)
-#define RT5645_PWR_ADC_R_BIT 1
#define RT5645_PWR_DAC_L2 (0x1 << 7)
#define RT5645_PWR_DAC_L2_BIT 7
#define RT5645_PWR_DAC_R2 (0x1 << 6)
@@ -942,10 +947,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 +1068,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)
@@ -1628,6 +1630,10 @@
#define RT5645_OT_P_NOR (0x0 << 10)
#define RT5645_OT_P_INV (0x1 << 10)
#define RT5645_IRQ_JD_1_1_EN (0x1 << 9)
+#define RT5645_JD_1_1_MASK (0x1 << 7)
+#define RT5645_JD_1_1_SFT 7
+#define RT5645_JD_1_1_NOR (0x0 << 7)
+#define RT5645_JD_1_1_INV (0x1 << 7)
/* IRQ Control 2 (0xbe) */
#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15)
@@ -1695,6 +1701,10 @@
#define RT5645_GP6_PIN_SFT 6
#define RT5645_GP6_PIN_GPIO6 (0x0 << 6)
#define RT5645_GP6_PIN_DMIC2_SDA (0x1 << 6)
+#define RT5645_I2S2_DAC_PIN_MASK (0x1 << 4)
+#define RT5645_I2S2_DAC_PIN_SFT 4
+#define RT5645_I2S2_DAC_PIN_I2S (0x0 << 4)
+#define RT5645_I2S2_DAC_PIN_GPIO (0x1 << 4)
#define RT5645_GP8_PIN_MASK (0x1 << 3)
#define RT5645_GP8_PIN_SFT 3
#define RT5645_GP8_PIN_GPIO8 (0x0 << 3)
@@ -2112,7 +2122,12 @@ enum {
/* General Control3 (0xfc) */
#define RT5645_JD_PSV_MODE (0x1 << 12)
#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11)
+#define RT5645_DET_CLK_MASK (0x3 << 9)
+#define RT5645_DET_CLK_DIS (0x0 << 9)
+#define RT5645_DET_CLK_MODE1 (0x1 << 9)
+#define RT5645_DET_CLK_MODE2 (0x2 << 9)
#define RT5645_MICINDET_MANU (0x1 << 7)
+#define RT5645_RING2_SLEEVE_GND (0x1 << 5)
/* Vendor ID (0xfd) */
#define RT5645_VER_C 0x2
@@ -2147,6 +2162,7 @@ enum {
};
enum {
+ RT5645_DMIC1_DISABLE,
RT5645_DMIC_DATA_IN2P,
RT5645_DMIC_DATA_GPIO6,
RT5645_DMIC_DATA_GPIO10,
@@ -2154,6 +2170,7 @@ enum {
};
enum {
+ RT5645_DMIC2_DISABLE,
RT5645_DMIC_DATA_IN2N,
RT5645_DMIC_DATA_GPIO5,
RT5645_DMIC_DATA_GPIO11,
@@ -2177,28 +2194,7 @@ enum {
int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
unsigned int filter_mask, unsigned int clk_src);
-struct rt5645_priv {
- struct snd_soc_codec *codec;
- struct rt5645_platform_data pdata;
- struct regmap *regmap;
- struct i2c_client *i2c;
- struct snd_soc_jack *hp_jack;
- struct snd_soc_jack *mic_jack;
- struct delayed_work jack_detect_work;
-
- int codec_type;
- int sysclk;
- int sysclk_src;
- int lrck[RT5645_AIFS];
- int bclk[RT5645_AIFS];
- int master[RT5645_AIFS];
-
- int pll_src;
- int pll_in;
- int pll_out;
-};
-
int 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/kernel/sound/soc/codecs/rt5651.c b/kernel/sound/soc/codecs/rt5651.c
index 9f4c7be6d..1d4031818 100644
--- a/kernel/sound/soc/codecs/rt5651.c
+++ b/kernel/sound/soc/codecs/rt5651.c
@@ -46,7 +46,7 @@ static const struct regmap_range_cfg rt5651_ranges[] = {
.window_len = 0x1, },
};
-static struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{RT5651_PR_BASE + 0x3d, 0x3e00},
};
@@ -292,16 +292,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
-static unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
/* Interface data select */
static const char * const rt5651_data_select[] = {
@@ -378,10 +377,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5651->sysclk);
+ int idx, rate;
+ rate = rt5651->sysclk / rl6231_get_pre_div(rt5651->regmap,
+ RT5651_ADDA_CLK1, RT5651_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@@ -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;
}
@@ -1770,7 +1769,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
if (ret != RT5651_DEVICE_ID_VALUE) {
dev_err(&i2c->dev,
- "Device with ID register %x is not rt5651\n", ret);
+ "Device with ID register %#x is not rt5651\n", ret);
return -ENODEV;
}
@@ -1807,7 +1806,6 @@ static int rt5651_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt5651_i2c_driver = {
.driver = {
.name = "rt5651",
- .owner = THIS_MODULE,
},
.probe = rt5651_i2c_probe,
.remove = rt5651_i2c_remove,
diff --git a/kernel/sound/soc/codecs/rt5670.c b/kernel/sound/soc/codecs/rt5670.c
index cc7f84a15..49a9e7049 100644
--- a/kernel/sound/soc/codecs/rt5670.c
+++ b/kernel/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_sequence 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;
@@ -593,16 +592,15 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
-static unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
/* Interface data select */
static const char * const rt5670_data_select[] = {
@@ -684,10 +682,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5670->sysclk);
+ int idx, rate;
+ rate = rt5670->sysclk / rl6231_get_pre_div(rt5670->regmap,
+ RT5670_ADDA_CLK1, RT5670_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@@ -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;
@@ -2721,7 +2720,7 @@ static int rt5670_resume(struct snd_soc_codec *codec)
#define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-static struct snd_soc_dai_ops rt5670_aif_dai_ops = {
+static const struct snd_soc_dai_ops rt5670_aif_dai_ops = {
.hw_params = rt5670_hw_params,
.set_fmt = rt5670_set_dai_fmt,
.set_sysclk = rt5670_set_dai_sysclk,
@@ -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},
{ },
};
@@ -2864,7 +2863,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
regmap_read(rt5670->regmap, RT5670_VENDOR_ID2, &val);
if (val != RT5670_DEVICE_ID) {
dev_err(&i2c->dev,
- "Device with ID register %x is not rt5670/72\n", val);
+ "Device with ID register %#x is not rt5670/72\n", val);
return -ENODEV;
}
@@ -3044,7 +3043,6 @@ static int rt5670_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt5670_i2c_driver = {
.driver = {
.name = "rt5670",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
.probe = rt5670_i2c_probe,
diff --git a/kernel/sound/soc/codecs/rt5670.h b/kernel/sound/soc/codecs/rt5670.h
index dc2b46236..3f1b0f1df 100644
--- a/kernel/sound/soc/codecs/rt5670.h
+++ b/kernel/sound/soc/codecs/rt5670.h
@@ -973,12 +973,12 @@
#define RT5670_SCLK_SRC_MCLK (0x0 << 14)
#define RT5670_SCLK_SRC_PLL1 (0x1 << 14)
#define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */
-#define RT5670_PLL1_SRC_MASK (0x3 << 12)
-#define RT5670_PLL1_SRC_SFT 12
-#define RT5670_PLL1_SRC_MCLK (0x0 << 12)
-#define RT5670_PLL1_SRC_BCLK1 (0x1 << 12)
-#define RT5670_PLL1_SRC_BCLK2 (0x2 << 12)
-#define RT5670_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5670_PLL1_SRC_MASK (0x7 << 11)
+#define RT5670_PLL1_SRC_SFT 11
+#define RT5670_PLL1_SRC_MCLK (0x0 << 11)
+#define RT5670_PLL1_SRC_BCLK1 (0x1 << 11)
+#define RT5670_PLL1_SRC_BCLK2 (0x2 << 11)
+#define RT5670_PLL1_SRC_BCLK3 (0x3 << 11)
#define RT5670_PLL1_PD_MASK (0x1 << 3)
#define RT5670_PLL1_PD_SFT 3
#define RT5670_PLL1_PD_1 (0x0 << 3)
diff --git a/kernel/sound/soc/codecs/rt5677-spi.c b/kernel/sound/soc/codecs/rt5677-spi.c
index ef6348cb9..91879ea95 100644
--- a/kernel/sound/soc/codecs/rt5677-spi.c
+++ b/kernel/sound/soc/codecs/rt5677-spi.c
@@ -31,84 +31,197 @@
#include "rt5677-spi.h"
+#define RT5677_SPI_BURST_LEN 240
+#define RT5677_SPI_HEADER 5
+#define RT5677_SPI_FREQ 6000000
+
+/* The AddressPhase and DataPhase of SPI commands are MSB first on the wire.
+ * DataPhase word size of 16-bit commands is 2 bytes.
+ * DataPhase word size of 32-bit commands is 4 bytes.
+ * DataPhase word size of burst commands is 8 bytes.
+ * The DSP CPU is little-endian.
+ */
+#define RT5677_SPI_WRITE_BURST 0x5
+#define RT5677_SPI_READ_BURST 0x4
+#define RT5677_SPI_WRITE_32 0x3
+#define RT5677_SPI_READ_32 0x2
+#define RT5677_SPI_WRITE_16 0x1
+#define RT5677_SPI_READ_16 0x0
+
static struct spi_device *g_spi;
+static DEFINE_MUTEX(spi_mutex);
-/**
- * rt5677_spi_write - Write data to SPI.
- * @txbuf: Data Buffer for writing.
- * @len: Data length.
+/* Select a suitable transfer command for the next transfer to ensure
+ * the transfer address is always naturally aligned while minimizing
+ * the total number of transfers required.
+ *
+ * 3 transfer commands are available:
+ * RT5677_SPI_READ/WRITE_16: Transfer 2 bytes
+ * RT5677_SPI_READ/WRITE_32: Transfer 4 bytes
+ * RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes
+ *
+ * For example, reading 260 bytes at 0x60030002 uses the following commands:
+ * 0x60030002 RT5677_SPI_READ_16 2 bytes
+ * 0x60030004 RT5677_SPI_READ_32 4 bytes
+ * 0x60030008 RT5677_SPI_READ_BURST 240 bytes
+ * 0x600300F8 RT5677_SPI_READ_BURST 8 bytes
+ * 0x60030100 RT5677_SPI_READ_32 4 bytes
+ * 0x60030104 RT5677_SPI_READ_16 2 bytes
*
+ * Input:
+ * @read: true for read commands; false for write commands
+ * @align: alignment of the next transfer address
+ * @remain: number of bytes remaining to transfer
*
- * Returns true for success.
+ * Output:
+ * @len: number of bytes to transfer with the selected command
+ * Returns the selected command
*/
-int rt5677_spi_write(u8 *txbuf, size_t len)
+static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len)
{
- int status;
-
- status = spi_write(g_spi, txbuf, len);
-
- if (status)
- dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
-
- return status;
+ u8 cmd;
+
+ if (align == 2 || align == 6 || remain == 2) {
+ cmd = RT5677_SPI_READ_16;
+ *len = 2;
+ } else if (align == 4 || remain <= 6) {
+ cmd = RT5677_SPI_READ_32;
+ *len = 4;
+ } else {
+ cmd = RT5677_SPI_READ_BURST;
+ *len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN);
+ }
+ return read ? cmd : cmd + 1;
}
-EXPORT_SYMBOL_GPL(rt5677_spi_write);
-/**
- * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address.
- * @addr: Start address.
- * @txbuf: Data Buffer for writng.
- * @len: Data length, it must be a multiple of 8.
- *
- *
- * Returns true for success.
+/* Copy dstlen bytes from src to dst, while reversing byte order for each word.
+ * If srclen < dstlen, zeros are padded.
*/
-int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
+static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen)
{
- u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
- u8 *write_buf;
- unsigned int i, end, offset = 0;
-
- write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
-
- if (write_buf == NULL)
- return -ENOMEM;
-
- while (offset < fw->size) {
- if (offset + RT5677_SPI_BUF_LEN <= fw->size)
- end = RT5677_SPI_BUF_LEN;
- else
- end = fw->size % RT5677_SPI_BUF_LEN;
-
- write_buf[0] = spi_cmd;
- write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
- write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
- write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
- write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
-
- for (i = 0; i < end; i += 8) {
- write_buf[i + 12] = fw->data[offset + i + 0];
- write_buf[i + 11] = fw->data[offset + i + 1];
- write_buf[i + 10] = fw->data[offset + i + 2];
- write_buf[i + 9] = fw->data[offset + i + 3];
- write_buf[i + 8] = fw->data[offset + i + 4];
- write_buf[i + 7] = fw->data[offset + i + 5];
- write_buf[i + 6] = fw->data[offset + i + 6];
- write_buf[i + 5] = fw->data[offset + i + 7];
+ u32 w, i, si;
+ u32 word_size = min_t(u32, dstlen, 8);
+
+ for (w = 0; w < dstlen; w += word_size) {
+ for (i = 0; i < word_size; i++) {
+ si = w + word_size - i - 1;
+ dst[w + i] = si < srclen ? src[si] : 0;
}
+ }
+}
- write_buf[end + 5] = spi_cmd;
+/* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */
+int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
+{
+ u32 offset;
+ int status = 0;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ /* +4 bytes is for the DummyPhase following the AddressPhase */
+ u8 header[RT5677_SPI_HEADER + 4];
+ u8 body[RT5677_SPI_BURST_LEN];
+ u8 spi_cmd;
+ u8 *cb = rxbuf;
+
+ if (!g_spi)
+ return -ENODEV;
+
+ if ((addr & 1) || (len & 1)) {
+ dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len);
+ return -EACCES;
+ }
- rt5677_spi_write(write_buf, end + 6);
+ memset(t, 0, sizeof(t));
+ t[0].tx_buf = header;
+ t[0].len = sizeof(header);
+ t[0].speed_hz = RT5677_SPI_FREQ;
+ t[1].rx_buf = body;
+ t[1].speed_hz = RT5677_SPI_FREQ;
+ spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t));
+
+ for (offset = 0; offset < len; offset += t[1].len) {
+ spi_cmd = rt5677_spi_select_cmd(true, (addr + offset) & 7,
+ len - offset, &t[1].len);
+
+ /* Construct SPI message header */
+ header[0] = spi_cmd;
+ header[1] = ((addr + offset) & 0xff000000) >> 24;
+ header[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ header[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ header[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ mutex_lock(&spi_mutex);
+ status |= spi_sync(g_spi, &m);
+ mutex_unlock(&spi_mutex);
+
+ /* Copy data back to caller buffer */
+ rt5677_spi_reverse(cb + offset, t[1].len, body, t[1].len);
+ }
+ return status;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_read);
- offset += RT5677_SPI_BUF_LEN;
+/* Write DSP address space using SPI. addr has to be 2-byte aligned.
+ * If len is not 2-byte aligned, an extra byte of zero is written at the end
+ * as padding.
+ */
+int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
+{
+ u32 offset, len_with_pad = len;
+ int status = 0;
+ struct spi_transfer t;
+ struct spi_message m;
+ /* +1 byte is for the DummyPhase following the DataPhase */
+ u8 buf[RT5677_SPI_HEADER + RT5677_SPI_BURST_LEN + 1];
+ u8 *body = buf + RT5677_SPI_HEADER;
+ u8 spi_cmd;
+ const u8 *cb = txbuf;
+
+ if (!g_spi)
+ return -ENODEV;
+
+ if (addr & 1) {
+ dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len);
+ return -EACCES;
}
- kfree(write_buf);
+ if (len & 1)
+ len_with_pad = len + 1;
+
+ memset(&t, 0, sizeof(t));
+ t.tx_buf = buf;
+ t.speed_hz = RT5677_SPI_FREQ;
+ spi_message_init_with_transfers(&m, &t, 1);
+
+ for (offset = 0; offset < len_with_pad;) {
+ spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7,
+ len_with_pad - offset, &t.len);
+
+ /* Construct SPI message header */
+ buf[0] = spi_cmd;
+ buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ /* Fetch data from caller buffer */
+ rt5677_spi_reverse(body, t.len, cb + offset, len - offset);
+ offset += t.len;
+ t.len += RT5677_SPI_HEADER + 1;
+
+ mutex_lock(&spi_mutex);
+ status |= spi_sync(g_spi, &m);
+ mutex_unlock(&spi_mutex);
+ }
+ return status;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_write);
- return 0;
+int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
+{
+ return rt5677_spi_write(addr, fw->data, fw->size);
}
-EXPORT_SYMBOL_GPL(rt5677_spi_burst_write);
+EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
static int rt5677_spi_probe(struct spi_device *spi)
{
@@ -119,7 +232,6 @@ static int rt5677_spi_probe(struct spi_device *spi)
static struct spi_driver rt5677_spi_driver = {
.driver = {
.name = "rt5677",
- .owner = THIS_MODULE,
},
.probe = rt5677_spi_probe,
};
diff --git a/kernel/sound/soc/codecs/rt5677-spi.h b/kernel/sound/soc/codecs/rt5677-spi.h
index ec41b2b3b..662db16cf 100644
--- a/kernel/sound/soc/codecs/rt5677-spi.h
+++ b/kernel/sound/soc/codecs/rt5677-spi.h
@@ -12,10 +12,8 @@
#ifndef __RT5677_SPI_H__
#define __RT5677_SPI_H__
-#define RT5677_SPI_BUF_LEN 240
-#define RT5677_SPI_CMD_BURST_WRITE 0x05
-
-int rt5677_spi_write(u8 *txbuf, size_t len);
-int rt5677_spi_burst_write(u32 addr, const struct firmware *fw);
+int rt5677_spi_read(u32 addr, void *rxbuf, size_t len);
+int rt5677_spi_write(u32 addr, const void *txbuf, size_t len);
+int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw);
#endif /* __RT5677_SPI_H__ */
diff --git a/kernel/sound/soc/codecs/rt5677.c b/kernel/sound/soc/codecs/rt5677.c
index 169aa471f..69d987a99 100644
--- a/kernel/sound/soc/codecs/rt5677.c
+++ b/kernel/sound/soc/codecs/rt5677.c
@@ -15,13 +15,12 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/firmware.h>
-#include <linux/gpio.h>
+#include <linux/property.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -54,7 +53,7 @@ static const struct regmap_range_cfg rt5677_ranges[] = {
},
};
-static const struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{RT5677_ASRC_12, 0x0018},
{RT5677_PR_BASE + 0x3d, 0x364d},
{RT5677_PR_BASE + 0x17, 0x4fc0},
@@ -746,14 +745,14 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
codec->dev);
if (ret == 0) {
- rt5677_spi_burst_write(0x50000000, rt5677->fw1);
+ rt5677_spi_write_firmware(0x50000000, rt5677->fw1);
release_firmware(rt5677->fw1);
}
ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
codec->dev);
if (ret == 0) {
- rt5677_spi_burst_write(0x60000000, rt5677->fw2);
+ rt5677_spi_write_firmware(0x60000000, rt5677->fw2);
release_firmware(rt5677->fw2);
}
@@ -789,16 +788,15 @@ static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
-static unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
- 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -820,7 +818,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;
@@ -917,8 +915,11 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- int idx = rl6231_calc_dmic_clk(rt5677->lrck[RT5677_AIF1] << 8);
+ int idx, rate;
+ rate = rt5677->sysclk / rl6231_get_pre_div(rt5677->regmap,
+ RT5677_CLK_TREE_CTRL1, RT5677_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@@ -1060,6 +1061,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 +1198,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,
@@ -1286,90 +1386,90 @@ static const struct snd_kcontrol_new rt5677_dac_r_mix[] = {
};
static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = {
- SOC_DAPM_SINGLE("ST R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = {
- SOC_DAPM_SINGLE("ST R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_STO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_STO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_STO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_STO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_R_SFT, 1, 1),
};
@@ -2479,7 +2579,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,
@@ -2496,6 +2596,21 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5677_filter_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(50);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU |
@@ -2972,19 +3087,26 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
/* DAC Mixer */
SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_S1F_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M2F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M2F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M3F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M3F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M4F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M4F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)),
@@ -3057,12 +3179,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 +4475,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 +4517,6 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -4606,22 +4727,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);
@@ -4665,8 +4787,8 @@ static int rt5677_remove(struct snd_soc_codec *codec)
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
- if (gpio_is_valid(rt5677->pow_ldo2))
- gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+ gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
+ gpiod_set_value_cansleep(rt5677->reset_pin, 0);
return 0;
}
@@ -4680,8 +4802,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
regcache_cache_only(rt5677->regmap, true);
regcache_mark_dirty(rt5677->regmap);
- if (gpio_is_valid(rt5677->pow_ldo2))
- gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+ gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
+ gpiod_set_value_cansleep(rt5677->reset_pin, 0);
}
return 0;
@@ -4692,10 +4814,10 @@ static int rt5677_resume(struct snd_soc_codec *codec)
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
if (!rt5677->dsp_vad_en) {
- if (gpio_is_valid(rt5677->pow_ldo2)) {
- gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
+ gpiod_set_value_cansleep(rt5677->pow_ldo2, 1);
+ gpiod_set_value_cansleep(rt5677->reset_pin, 1);
+ if (rt5677->pow_ldo2 || rt5677->reset_pin)
msleep(10);
- }
regcache_cache_only(rt5677->regmap, false);
regcache_sync(rt5677->regmap);
@@ -4757,7 +4879,7 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val)
#define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
+static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.hw_params = rt5677_hw_params,
.set_fmt = rt5677_set_dai_fmt,
.set_sysclk = rt5677_set_dai_sysclk,
@@ -4918,40 +5040,29 @@ static const struct i2c_device_id rt5677_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
-static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
+static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
+ struct device *dev)
{
- rt5677->pdata.in1_diff = of_property_read_bool(np,
- "realtek,in1-differential");
- rt5677->pdata.in2_diff = of_property_read_bool(np,
- "realtek,in2-differential");
- rt5677->pdata.lout1_diff = of_property_read_bool(np,
- "realtek,lout1-differential");
- rt5677->pdata.lout2_diff = of_property_read_bool(np,
- "realtek,lout2-differential");
- rt5677->pdata.lout3_diff = of_property_read_bool(np,
- "realtek,lout3-differential");
-
- rt5677->pow_ldo2 = of_get_named_gpio(np,
- "realtek,pow-ldo2-gpio", 0);
-
- /*
- * POW_LDO2 is optional (it may be statically tied on the board).
- * -ENOENT means that the property doesn't exist, i.e. there is no
- * GPIO, so is not an error. Any other error code means the property
- * exists, but could not be parsed.
- */
- if (!gpio_is_valid(rt5677->pow_ldo2) &&
- (rt5677->pow_ldo2 != -ENOENT))
- return rt5677->pow_ldo2;
-
- of_property_read_u8_array(np, "realtek,gpio-config",
- rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
-
- of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio);
- of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio);
- of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio);
-
- return 0;
+ rt5677->pdata.in1_diff = device_property_read_bool(dev,
+ "realtek,in1-differential");
+ rt5677->pdata.in2_diff = device_property_read_bool(dev,
+ "realtek,in2-differential");
+ rt5677->pdata.lout1_diff = device_property_read_bool(dev,
+ "realtek,lout1-differential");
+ rt5677->pdata.lout2_diff = device_property_read_bool(dev,
+ "realtek,lout2-differential");
+ rt5677->pdata.lout3_diff = device_property_read_bool(dev,
+ "realtek,lout3-differential");
+
+ device_property_read_u8_array(dev, "realtek,gpio-config",
+ rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
+
+ device_property_read_u32(dev, "realtek,jd1-gpio",
+ &rt5677->pdata.jd1_gpio);
+ device_property_read_u32(dev, "realtek,jd2-gpio",
+ &rt5677->pdata.jd2_gpio);
+ device_property_read_u32(dev, "realtek,jd3-gpio",
+ &rt5677->pdata.jd3_gpio);
}
static struct regmap_irq rt5677_irqs[] = {
@@ -5034,27 +5145,29 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5677->pdata = *pdata;
+ else
+ rt5677_read_device_properties(rt5677, &i2c->dev);
- if (i2c->dev.of_node) {
- ret = rt5677_parse_dt(rt5677, i2c->dev.of_node);
- if (ret) {
- dev_err(&i2c->dev, "Failed to parse device tree: %d\n",
- ret);
- return ret;
- }
- } else {
- rt5677->pow_ldo2 = -EINVAL;
+ /* pow-ldo2 and reset are optional. The codec pins may be statically
+ * connected on the board without gpios. If the gpio device property
+ * isn't specified, devm_gpiod_get_optional returns NULL.
+ */
+ rt5677->pow_ldo2 = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,pow-ldo2", GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5677->pow_ldo2)) {
+ ret = PTR_ERR(rt5677->pow_ldo2);
+ dev_err(&i2c->dev, "Failed to request POW_LDO2: %d\n", ret);
+ return ret;
+ }
+ rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5677->reset_pin)) {
+ ret = PTR_ERR(rt5677->reset_pin);
+ dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret);
+ return ret;
}
- if (gpio_is_valid(rt5677->pow_ldo2)) {
- ret = devm_gpio_request_one(&i2c->dev, rt5677->pow_ldo2,
- GPIOF_OUT_INIT_HIGH,
- "RT5677 POW_LDO2");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request POW_LDO2 %d: %d\n",
- rt5677->pow_ldo2, ret);
- return ret;
- }
+ if (rt5677->pow_ldo2 || rt5677->reset_pin) {
/* Wait a while until I2C bus becomes available. The datasheet
* does not specify the exact we should wait but startup
* sequence mentiones at least a few milliseconds.
@@ -5082,7 +5195,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val);
if (val != RT5677_DEVICE_ID) {
dev_err(&i2c->dev,
- "Device with ID register %x is not rt5677\n", val);
+ "Device with ID register %#x is not rt5677\n", val);
return -ENODEV;
}
@@ -5146,7 +5259,6 @@ static int rt5677_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
.name = "rt5677",
- .owner = THIS_MODULE,
},
.probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
diff --git a/kernel/sound/soc/codecs/rt5677.h b/kernel/sound/soc/codecs/rt5677.h
index 9dceb41d1..d46855a42 100644
--- a/kernel/sound/soc/codecs/rt5677.h
+++ b/kernel/sound/soc/codecs/rt5677.h
@@ -14,6 +14,7 @@
#include <sound/rt5677.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h>
/* Info */
#define RT5677_RESET 0x00
@@ -1446,6 +1447,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 +1755,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 {
@@ -1761,7 +1776,8 @@ struct rt5677_priv {
int pll_src;
int pll_in;
int pll_out;
- int pow_ldo2; /* POW_LDO2 pin */
+ struct gpio_desc *pow_ldo2; /* POW_LDO2 pin */
+ struct gpio_desc *reset_pin; /* RESET pin */
enum rt5677_type type;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
diff --git a/kernel/sound/soc/codecs/sgtl5000.c b/kernel/sound/soc/codecs/sgtl5000.c
index 3593a1496..08b404606 100644
--- a/kernel/sound/soc/codecs/sgtl5000.c
+++ b/kernel/sound/soc/codecs/sgtl5000.c
@@ -189,6 +189,7 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+ msleep(400);
break;
case SND_SOC_DAPM_PRE_PMD:
@@ -406,11 +407,10 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
/* tlv for mic gain, 0db 20db 30db 40db */
-static const unsigned int mic_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
- 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
-};
+ 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0)
+);
/* tlv for hp volume, -51.5db to 12.0db, step .5db */
static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
@@ -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;
}
@@ -1339,8 +1377,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
- SGTL5000_BIAS_R_MASK,
- sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT);
+ SGTL5000_BIAS_VOLT_MASK,
+ sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT);
/*
* disable DAP
* TODO:
@@ -1512,7 +1550,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
else {
sgtl5000->micbias_voltage = 0;
dev_err(&client->dev,
- "Unsuitable MicBias resistor\n");
+ "Unsuitable MicBias voltage\n");
}
} else {
sgtl5000->micbias_voltage = 0;
@@ -1563,7 +1601,6 @@ MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
static struct i2c_driver sgtl5000_i2c_driver = {
.driver = {
.name = "sgtl5000",
- .owner = THIS_MODULE,
.of_match_table = sgtl5000_dt_ids,
},
.probe = sgtl5000_i2c_probe,
diff --git a/kernel/sound/soc/codecs/sgtl5000.h b/kernel/sound/soc/codecs/sgtl5000.h
index bd7a344bf..1c317de26 100644
--- a/kernel/sound/soc/codecs/sgtl5000.h
+++ b/kernel/sound/soc/codecs/sgtl5000.h
@@ -275,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 0
+#define SGTL5000_SMALL_POP 1
/*
* SGTL5000_CHIP_MIC_CTRL
diff --git a/kernel/sound/soc/codecs/si476x.c b/kernel/sound/soc/codecs/si476x.c
index 3e7296428..a8402d0af 100644
--- a/kernel/sound/soc/codecs/si476x.c
+++ b/kernel/sound/soc/codecs/si476x.c
@@ -208,7 +208,7 @@ out:
return err;
}
-static struct snd_soc_dai_ops si476x_dai_ops = {
+static const struct snd_soc_dai_ops si476x_dai_ops = {
.hw_params = si476x_codec_hw_params,
.set_fmt = si476x_codec_set_dai_fmt,
};
diff --git a/kernel/sound/soc/codecs/sirf-audio-codec.c b/kernel/sound/soc/codecs/sirf-audio-codec.c
index 0a8e43c98..6bfd25c28 100644
--- a/kernel/sound/soc/codecs/sirf-audio-codec.c
+++ b/kernel/sound/soc/codecs/sirf-audio-codec.c
@@ -370,11 +370,11 @@ static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
return 0;
}
-struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
+static const struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
.trigger = sirf_audio_codec_trigger,
};
-struct snd_soc_dai_driver sirf_audio_codec_dai = {
+static struct snd_soc_dai_driver sirf_audio_codec_dai = {
.name = "sirf-audio-codec",
.playback = {
.stream_name = "Playback",
@@ -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/kernel/sound/soc/codecs/sn95031.c b/kernel/sound/soc/codecs/sn95031.c
index 7947c0ebb..3a7de0159 100644
--- a/kernel/sound/soc/codecs/sn95031.c
+++ b/kernel/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/kernel/sound/soc/codecs/ssm2518.c b/kernel/sound/soc/codecs/ssm2518.c
index 67ea55adb..86b81a60a 100644
--- a/kernel/sound/soc/codecs/ssm2518.c
+++ b/kernel/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,
@@ -728,17 +723,11 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = {
.num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
};
-static bool ssm2518_register_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
static const struct regmap_config ssm2518_regmap_config = {
.val_bits = 8,
.reg_bits = 8,
.max_register = SSM2518_REG_DRC_9,
- .volatile_reg = ssm2518_register_volatile,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = ssm2518_reg_defaults,
@@ -811,6 +800,14 @@ static int ssm2518_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id ssm2518_dt_ids[] = {
+ { .compatible = "adi,ssm2518", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssm2518_dt_ids);
+#endif
+
static const struct i2c_device_id ssm2518_i2c_ids[] = {
{ "ssm2518", 0 },
{ }
@@ -820,7 +817,7 @@ MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
static struct i2c_driver ssm2518_driver = {
.driver = {
.name = "ssm2518",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ssm2518_dt_ids),
},
.probe = ssm2518_i2c_probe,
.remove = ssm2518_i2c_remove,
diff --git a/kernel/sound/soc/codecs/ssm2602-i2c.c b/kernel/sound/soc/codecs/ssm2602-i2c.c
index 0d9779d6b..173ba85ff 100644
--- a/kernel/sound/soc/codecs/ssm2602-i2c.c
+++ b/kernel/sound/soc/codecs/ssm2602-i2c.c
@@ -52,7 +52,6 @@ MODULE_DEVICE_TABLE(of, ssm2602_of_match);
static struct i2c_driver ssm2602_i2c_driver = {
.driver = {
.name = "ssm2602",
- .owner = THIS_MODULE,
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_i2c_probe,
diff --git a/kernel/sound/soc/codecs/ssm2602-spi.c b/kernel/sound/soc/codecs/ssm2602-spi.c
index b5df14fbe..842f37304 100644
--- a/kernel/sound/soc/codecs/ssm2602-spi.c
+++ b/kernel/sound/soc/codecs/ssm2602-spi.c
@@ -35,7 +35,6 @@ MODULE_DEVICE_TABLE(of, ssm2602_of_match);
static struct spi_driver ssm2602_spi_driver = {
.driver = {
.name = "ssm2602",
- .owner = THIS_MODULE,
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_spi_probe,
diff --git a/kernel/sound/soc/codecs/ssm2602.c b/kernel/sound/soc/codecs/ssm2602.c
index 314eaece1..4452fea0b 100644
--- a/kernel/sound/soc/codecs/ssm2602.c
+++ b/kernel/sound/soc/codecs/ssm2602.c
@@ -75,11 +75,10 @@ static const struct soc_enum ssm2602_enum[] = {
ssm2602_deemph),
};
-static const unsigned int ssm260x_outmix_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(ssm260x_outmix_tlv,
0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
- 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
-};
+ 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0)
+);
static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
@@ -473,7 +472,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -524,8 +522,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 +547,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/kernel/sound/soc/codecs/ssm4567.c b/kernel/sound/soc/codecs/ssm4567.c
index f7549cc7e..e619d5651 100644
--- a/kernel/sound/soc/codecs/ssm4567.c
+++ b/kernel/sound/soc/codecs/ssm4567.c
@@ -10,6 +10,7 @@
* Licensed under the GPL-2.
*/
+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
@@ -173,6 +174,12 @@ static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1,
&ssm4567_amplifier_boost_control),
+ SND_SOC_DAPM_SIGGEN("Sense"),
+
+ SND_SOC_DAPM_PGA("Current Sense", SSM4567_REG_POWER_CTRL, 4, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Voltage Sense", SSM4567_REG_POWER_CTRL, 5, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("VBAT Sense", SSM4567_REG_POWER_CTRL, 6, 1, NULL, 0),
+
SND_SOC_DAPM_OUTPUT("OUT"),
};
@@ -180,6 +187,13 @@ static const struct snd_soc_dapm_route ssm4567_routes[] = {
{ "OUT", NULL, "Amplifier Boost" },
{ "Amplifier Boost", "Switch", "DAC" },
{ "OUT", NULL, "DAC" },
+
+ { "Current Sense", NULL, "Sense" },
+ { "Voltage Sense", NULL, "Sense" },
+ { "VBAT Sense", NULL, "Sense" },
+ { "Capture Sense", NULL, "Current Sense" },
+ { "Capture Sense", NULL, "Voltage Sense" },
+ { "Capture Sense", NULL, "VBAT Sense" },
};
static int ssm4567_hw_params(struct snd_pcm_substream *substream,
@@ -359,7 +373,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:
@@ -367,12 +381,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 = {
@@ -392,6 +401,14 @@ static struct snd_soc_dai_driver ssm4567_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32,
},
+ .capture = {
+ .stream_name = "Capture Sense",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32,
+ },
.ops = &ssm4567_dai_ops,
};
@@ -461,10 +478,20 @@ static const struct i2c_device_id ssm4567_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
+#ifdef CONFIG_ACPI
+
+static const struct acpi_device_id ssm4567_acpi_match[] = {
+ { "INT343B", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match);
+
+#endif
+
static struct i2c_driver ssm4567_driver = {
.driver = {
.name = "ssm4567",
- .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
},
.probe = ssm4567_i2c_probe,
.remove = ssm4567_i2c_remove,
diff --git a/kernel/sound/soc/codecs/sta32x.c b/kernel/sound/soc/codecs/sta32x.c
index 007a0e3bc..a9844b2ac 100644
--- a/kernel/sound/soc/codecs/sta32x.c
+++ b/kernel/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++)
@@ -1151,7 +1144,6 @@ MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
static struct i2c_driver sta32x_i2c_driver = {
.driver = {
.name = "sta32x",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(st32x_dt_ids),
},
.probe = sta32x_i2c_probe,
diff --git a/kernel/sound/soc/codecs/sta350.c b/kernel/sound/soc/codecs/sta350.c
index 669e32282..33a4612f0 100644
--- a/kernel/sound/soc/codecs/sta350.c
+++ b/kernel/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);
@@ -1265,7 +1264,6 @@ MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
static struct i2c_driver sta350_i2c_driver = {
.driver = {
.name = "sta350",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(st350_dt_ids),
},
.probe = sta350_i2c_probe,
diff --git a/kernel/sound/soc/codecs/sta529.c b/kernel/sound/soc/codecs/sta529.c
index b0f436d10..2cdaca943 100644
--- a/kernel/sound/soc/codecs/sta529.c
+++ b/kernel/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;
}
@@ -345,9 +339,6 @@ static int sta529_i2c_probe(struct i2c_client *i2c,
struct sta529 *sta529;
int ret;
- if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -EINVAL;
-
sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL);
if (!sta529)
return -ENOMEM;
@@ -385,7 +376,6 @@ MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
static struct i2c_driver sta529_i2c_driver = {
.driver = {
.name = "sta529",
- .owner = THIS_MODULE,
},
.probe = sta529_i2c_probe,
.remove = sta529_i2c_remove,
diff --git a/kernel/sound/soc/codecs/stac9766.c b/kernel/sound/soc/codecs/stac9766.c
index 6464caf72..0945c51df 100644
--- a/kernel/sound/soc/codecs/stac9766.c
+++ b/kernel/sound/soc/codecs/stac9766.c
@@ -28,6 +28,9 @@
#include "stac9766.h"
+#define STAC9766_VENDOR_ID 0x83847666
+#define STAC9766_VENDOR_ID_MASK 0xffffffff
+
/*
* STAC9766 register cache
*/
@@ -236,49 +239,15 @@ 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;
-}
-
-static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
-
- if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(ac97);
- if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
- return 1;
- }
-
- soc_ac97_ops->reset(ac97);
- if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(ac97);
- if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
- return -EIO;
return 0;
}
static int stac9766_codec_resume(struct snd_soc_codec *codec)
{
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- u16 id, reset;
- reset = 0;
- /* give the codec an AC97 warm reset to start the link */
-reset:
- if (reset > 5) {
- dev_err(codec->dev, "Failed to resume\n");
- return -EIO;
- }
- ac97->bus->ops->warm_reset(ac97);
- id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
- if (id != 0x4c13) {
- stac9766_reset(codec, 0);
- reset++;
- goto reset;
- }
-
- return 0;
+ return snd_ac97_reset(ac97, true, STAC9766_VENDOR_ID,
+ STAC9766_VENDOR_ID_MASK);
}
static const struct snd_soc_dai_ops stac9766_dai_ops_analog = {
@@ -321,7 +290,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,
@@ -331,28 +300,15 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
static int stac9766_codec_probe(struct snd_soc_codec *codec)
{
struct snd_ac97 *ac97;
- int ret = 0;
- ac97 = snd_soc_new_ac97_codec(codec);
+ ac97 = snd_soc_new_ac97_codec(codec, STAC9766_VENDOR_ID,
+ STAC9766_VENDOR_ID_MASK);
if (IS_ERR(ac97))
return PTR_ERR(ac97);
snd_soc_codec_set_drvdata(codec, ac97);
- /* do a cold reset for the controller and then try
- * a warm reset followed by an optional cold reset for codec */
- stac9766_reset(codec, 0);
- ret = stac9766_reset(codec, 1);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
- goto codec_err;
- }
-
return 0;
-
-codec_err:
- snd_soc_free_ac97_codec(ac97);
- return ret;
}
static int stac9766_codec_remove(struct snd_soc_codec *codec)
diff --git a/kernel/sound/soc/codecs/sti-sas.c b/kernel/sound/soc/codecs/sti-sas.c
new file mode 100644
index 000000000..160d61a66
--- /dev/null
+++ b/kernel/sound/soc/codecs/sti-sas.c
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+/* chipID supported */
+#define CHIPID_STIH416 0
+#define CHIPID_STIH407 1
+
+/* DAC definitions */
+
+/* stih416 DAC registers */
+/* sysconf 2517: Audio-DAC-Control */
+#define STIH416_AUDIO_DAC_CTRL 0x00000814
+/* sysconf 2519: Audio-Gue-Control */
+#define STIH416_AUDIO_GLUE_CTRL 0x0000081C
+
+#define STIH416_DAC_NOT_STANDBY 0x3
+#define STIH416_DAC_SOFTMUTE 0x4
+#define STIH416_DAC_ANA_NOT_PWR 0x5
+#define STIH416_DAC_NOT_PNDBG 0x6
+
+#define STIH416_DAC_NOT_STANDBY_MASK BIT(STIH416_DAC_NOT_STANDBY)
+#define STIH416_DAC_SOFTMUTE_MASK BIT(STIH416_DAC_SOFTMUTE)
+#define STIH416_DAC_ANA_NOT_PWR_MASK BIT(STIH416_DAC_ANA_NOT_PWR)
+#define STIH416_DAC_NOT_PNDBG_MASK BIT(STIH416_DAC_NOT_PNDBG)
+
+/* stih407 DAC registers */
+/* sysconf 5041: Audio-Gue-Control */
+#define STIH407_AUDIO_GLUE_CTRL 0x000000A4
+/* sysconf 5042: Audio-DAC-Control */
+#define STIH407_AUDIO_DAC_CTRL 0x000000A8
+
+/* DAC definitions */
+#define STIH407_DAC_SOFTMUTE 0x0
+#define STIH407_DAC_STANDBY_ANA 0x1
+#define STIH407_DAC_STANDBY 0x2
+
+#define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE)
+#define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA)
+#define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY)
+
+/* SPDIF definitions */
+#define SPDIF_BIPHASE_ENABLE 0x6
+#define SPDIF_BIPHASE_IDLE 0x7
+
+#define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE)
+#define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE)
+
+enum {
+ STI_SAS_DAI_SPDIF_OUT,
+ STI_SAS_DAI_ANALOG_OUT,
+};
+
+static const struct reg_default stih416_sas_reg_defaults[] = {
+ { STIH407_AUDIO_GLUE_CTRL, 0x00000040 },
+ { STIH407_AUDIO_DAC_CTRL, 0x000000000 },
+};
+
+static const struct reg_default stih407_sas_reg_defaults[] = {
+ { STIH416_AUDIO_DAC_CTRL, 0x000000000 },
+ { STIH416_AUDIO_GLUE_CTRL, 0x00000040 },
+};
+
+struct sti_dac_audio {
+ struct regmap *regmap;
+ struct regmap *virt_regmap;
+ struct regmap_field **field;
+ struct reset_control *rst;
+ int mclk;
+};
+
+struct sti_spdif_audio {
+ struct regmap *regmap;
+ struct regmap_field **field;
+ int mclk;
+};
+
+/* device data structure */
+struct sti_sas_dev_data {
+ const int chipid; /* IC version */
+ const struct regmap_config *regmap;
+ const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */
+ const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */
+ const int num_dapm_widgets; /* dapms declaration */
+ const struct snd_soc_dapm_route *dapm_routes; /* route declaration */
+ const int num_dapm_routes; /* route declaration */
+};
+
+/* driver data structure */
+struct sti_sas_data {
+ struct device *dev;
+ const struct sti_sas_dev_data *dev_data;
+ struct sti_dac_audio dac;
+ struct sti_spdif_audio spdif;
+};
+
+/* Read a register from the sysconf reg bank */
+static int sti_sas_read_reg(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct sti_sas_data *drvdata = context;
+ int status;
+ u32 val;
+
+ status = regmap_read(drvdata->dac.regmap, reg, &val);
+ *value = (unsigned int)val;
+
+ return status;
+}
+
+/* Read a register from the sysconf reg bank */
+static int sti_sas_write_reg(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct sti_sas_data *drvdata = context;
+ int status;
+
+ status = regmap_write(drvdata->dac.regmap, reg, value);
+
+ return status;
+}
+
+static int sti_sas_init_sas_registers(struct snd_soc_codec *codec,
+ struct sti_sas_data *data)
+{
+ int ret;
+ /*
+ * DAC and SPDIF are activated by default
+ * put them in IDLE to save power
+ */
+
+ /* Initialise bi-phase formatter to disabled */
+ ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ SPDIF_BIPHASE_ENABLE_MASK, 0);
+
+ if (!ret)
+ /* Initialise bi-phase formatter idle value to 0 */
+ ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ SPDIF_BIPHASE_IDLE_MASK, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to update SPDIF registers");
+ return ret;
+ }
+
+ /* Init DAC configuration */
+ switch (data->dev_data->chipid) {
+ case CHIPID_STIH407:
+ /* init configuration */
+ ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_STANDBY_MASK,
+ STIH407_DAC_STANDBY_MASK);
+
+ if (!ret)
+ ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_STANDBY_ANA_MASK,
+ STIH407_DAC_STANDBY_ANA_MASK);
+ if (!ret)
+ ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_SOFTMUTE_MASK,
+ STIH407_DAC_SOFTMUTE_MASK);
+ break;
+ case CHIPID_STIH416:
+ ret = snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_NOT_STANDBY_MASK, 0);
+ if (!ret)
+ ret = snd_soc_update_bits(codec,
+ STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_ANA_NOT_PWR, 0);
+ if (!ret)
+ ret = snd_soc_update_bits(codec,
+ STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_NOT_PNDBG_MASK,
+ 0);
+ if (!ret)
+ ret = snd_soc_update_bits(codec,
+ STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_SOFTMUTE_MASK,
+ STIH416_DAC_SOFTMUTE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to update DAC registers");
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * DAC
+ */
+static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ /* Sanity check only */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ dev_err(dai->codec->dev,
+ "%s: ERROR: Unsupporter master mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stih416_dac_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+
+ /* Get reset control */
+ dac->rst = devm_reset_control_get(codec->dev, "dac_rst");
+ if (IS_ERR(dac->rst)) {
+ dev_err(dai->codec->dev,
+ "%s: ERROR: DAC reset control not defined !\n",
+ __func__);
+ dac->rst = NULL;
+ return -EFAULT;
+ }
+ /* Put the DAC into reset */
+ reset_control_assert(dac->rst);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA("DAC bandgap", STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_NOT_PNDBG_MASK, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_ANA_NOT_PWR, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_NOT_STANDBY, 0),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+};
+
+static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = {
+ SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_STANDBY_ANA, 1, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_STANDBY, 1),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+};
+
+static const struct snd_soc_dapm_route stih416_sas_route[] = {
+ {"DAC Output", NULL, "DAC bandgap"},
+ {"DAC Output", NULL, "DAC standby ana"},
+ {"DAC standby ana", NULL, "DAC standby"},
+};
+
+static const struct snd_soc_dapm_route stih407_sas_route[] = {
+ {"DAC Output", NULL, "DAC standby ana"},
+ {"DAC standby ana", NULL, "DAC standby"},
+};
+
+static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_SOFTMUTE_MASK,
+ STIH416_DAC_SOFTMUTE_MASK);
+ } else {
+ return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL,
+ STIH416_DAC_SOFTMUTE_MASK, 0);
+ }
+}
+
+static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_SOFTMUTE_MASK,
+ STIH407_DAC_SOFTMUTE_MASK);
+ } else {
+ return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_SOFTMUTE_MASK,
+ 0);
+ }
+}
+
+/*
+ * SPDIF
+ */
+static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ dev_err(dai->codec->dev,
+ "%s: ERROR: Unsupporter master mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * sti_sas_spdif_trigger:
+ * Trigger function is used to ensure that BiPhase Formater is disabled
+ * before CPU dai is stopped.
+ * This is mandatory to avoid that BPF is stalled
+ */
+static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ SPDIF_BIPHASE_ENABLE_MASK,
+ SPDIF_BIPHASE_ENABLE_MASK);
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ SPDIF_BIPHASE_ENABLE_MASK,
+ 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static bool sti_sas_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == STIH407_AUDIO_GLUE_CTRL)
+ return true;
+
+ return false;
+}
+
+/*
+ * CODEC DAIS
+ */
+
+/*
+ * sti_sas_set_sysclk:
+ * get MCLK input frequency to check that MCLK-FS ratio is coherent
+ */
+static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+
+ if (dir == SND_SOC_CLOCK_OUT)
+ return 0;
+
+ if (clk_id != 0)
+ return -EINVAL;
+
+ switch (dai->id) {
+ case STI_SAS_DAI_SPDIF_OUT:
+ drvdata->spdif.mclk = freq;
+ break;
+
+ case STI_SAS_DAI_ANALOG_OUT:
+ drvdata->dac.mclk = freq;
+ break;
+ }
+
+ return 0;
+}
+
+static int sti_sas_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ switch (dai->id) {
+ case STI_SAS_DAI_SPDIF_OUT:
+ if ((drvdata->spdif.mclk / runtime->rate) != 128) {
+ dev_err(codec->dev, "unexpected mclk-fs ratio");
+ return -EINVAL;
+ }
+ break;
+ case STI_SAS_DAI_ANALOG_OUT:
+ if ((drvdata->dac.mclk / runtime->rate) != 256) {
+ dev_err(codec->dev, "unexpected mclk-fs ratio");
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops stih416_dac_ops = {
+ .set_fmt = sti_sas_dac_set_fmt,
+ .mute_stream = stih416_sas_dac_mute,
+ .prepare = sti_sas_prepare,
+ .set_sysclk = sti_sas_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops stih407_dac_ops = {
+ .set_fmt = sti_sas_dac_set_fmt,
+ .mute_stream = stih407_sas_dac_mute,
+ .prepare = sti_sas_prepare,
+ .set_sysclk = sti_sas_set_sysclk,
+};
+
+static const struct regmap_config stih407_sas_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+
+ .max_register = STIH407_AUDIO_DAC_CTRL,
+ .reg_defaults = stih407_sas_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults),
+ .volatile_reg = sti_sas_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_read = sti_sas_read_reg,
+ .reg_write = sti_sas_write_reg,
+};
+
+static const struct regmap_config stih416_sas_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+
+ .max_register = STIH416_AUDIO_DAC_CTRL,
+ .reg_defaults = stih416_sas_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(stih416_sas_reg_defaults),
+ .volatile_reg = sti_sas_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_read = sti_sas_read_reg,
+ .reg_write = sti_sas_write_reg,
+};
+
+static const struct sti_sas_dev_data stih416_data = {
+ .chipid = CHIPID_STIH416,
+ .regmap = &stih416_sas_regmap,
+ .dac_ops = &stih416_dac_ops,
+ .dapm_widgets = stih416_sas_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets),
+ .dapm_routes = stih416_sas_route,
+ .num_dapm_routes = ARRAY_SIZE(stih416_sas_route),
+};
+
+static const struct sti_sas_dev_data stih407_data = {
+ .chipid = CHIPID_STIH407,
+ .regmap = &stih407_sas_regmap,
+ .dac_ops = &stih407_dac_ops,
+ .dapm_widgets = stih407_sas_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets),
+ .dapm_routes = stih407_sas_route,
+ .num_dapm_routes = ARRAY_SIZE(stih407_sas_route),
+};
+
+static struct snd_soc_dai_driver sti_sas_dai[] = {
+ {
+ .name = "sas-dai-spdif-out",
+ .id = STI_SAS_DAI_SPDIF_OUT,
+ .playback = {
+ .stream_name = "spdif_p",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = (struct snd_soc_dai_ops[]) {
+ {
+ .set_fmt = sti_sas_spdif_set_fmt,
+ .trigger = sti_sas_spdif_trigger,
+ .set_sysclk = sti_sas_set_sysclk,
+ .prepare = sti_sas_prepare,
+ }
+ },
+ },
+ {
+ .name = "sas-dai-dac",
+ .id = STI_SAS_DAI_ANALOG_OUT,
+ .playback = {
+ .stream_name = "dac_p",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ },
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int sti_sas_resume(struct snd_soc_codec *codec)
+{
+ struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+
+ return sti_sas_init_sas_registers(codec, drvdata);
+}
+#else
+#define sti_sas_resume NULL
+#endif
+
+static int sti_sas_codec_probe(struct snd_soc_codec *codec)
+{
+ struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+ int ret;
+
+ ret = sti_sas_init_sas_registers(codec, drvdata);
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver sti_sas_driver = {
+ .probe = sti_sas_codec_probe,
+ .resume = sti_sas_resume,
+};
+
+static const struct of_device_id sti_sas_dev_match[] = {
+ {
+ .compatible = "st,stih416-sas-codec",
+ .data = &stih416_data,
+ },
+ {
+ .compatible = "st,stih407-sas-codec",
+ .data = &stih407_data,
+ },
+ {},
+};
+
+static int sti_sas_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *pnode = pdev->dev.of_node;
+ struct sti_sas_data *drvdata;
+ const struct of_device_id *of_id;
+
+ /* Allocate device structure */
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ /* Populate data structure depending on compatibility */
+ of_id = of_match_node(sti_sas_dev_match, pnode);
+ if (!of_id->data) {
+ dev_err(&pdev->dev, "data associated to device is missing");
+ return -EINVAL;
+ }
+
+ drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data;
+
+ /* Initialise device structure */
+ drvdata->dev = &pdev->dev;
+
+ /* Request the DAC & SPDIF registers memory region */
+ drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata,
+ drvdata->dev_data->regmap);
+ if (IS_ERR(drvdata->dac.virt_regmap)) {
+ dev_err(&pdev->dev, "audio registers not enabled\n");
+ return PTR_ERR(drvdata->dac.virt_regmap);
+ }
+
+ /* Request the syscon region */
+ drvdata->dac.regmap =
+ syscon_regmap_lookup_by_phandle(pnode, "st,syscfg");
+ if (IS_ERR(drvdata->dac.regmap)) {
+ dev_err(&pdev->dev, "syscon registers not available\n");
+ return PTR_ERR(drvdata->dac.regmap);
+ }
+ drvdata->spdif.regmap = drvdata->dac.regmap;
+
+ /* Set DAC dai probe */
+ if (drvdata->dev_data->chipid == CHIPID_STIH416)
+ sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe;
+
+ sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops;
+
+ /* Set dapms*/
+ sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets;
+ sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets;
+
+ sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes;
+ sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes;
+
+ /* Store context */
+ dev_set_drvdata(&pdev->dev, drvdata);
+
+ return snd_soc_register_codec(&pdev->dev, &sti_sas_driver,
+ sti_sas_dai,
+ ARRAY_SIZE(sti_sas_dai));
+}
+
+static int sti_sas_driver_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver sti_sas_platform_driver = {
+ .driver = {
+ .name = "sti-sas-codec",
+ .of_match_table = sti_sas_dev_match,
+ },
+ .probe = sti_sas_driver_probe,
+ .remove = sti_sas_driver_remove,
+};
+
+module_platform_driver(sti_sas_platform_driver);
+
+MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms");
+MODULE_AUTHOR("Arnaud.pouliquen@st.com");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/tas2552.c b/kernel/sound/soc/codecs/tas2552.c
index 18558595b..cc1d3981f 100644
--- a/kernel/sound/soc/codecs/tas2552.c
+++ b/kernel/sound/soc/codecs/tas2552.c
@@ -34,18 +34,19 @@
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <sound/tas2552-plat.h>
+#include <dt-bindings/sound/tas2552.h>
#include "tas2552.h"
-static struct reg_default tas2552_reg_defs[] = {
+static const struct reg_default tas2552_reg_defs[] = {
{TAS2552_CFG_1, 0x22},
{TAS2552_CFG_3, 0x80},
{TAS2552_DOUT, 0x00},
{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,128 +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 (!tas_data->codec)
+ 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;
}
@@ -246,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;
+ }
+
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ return 0;
+}
- tas2552->mclk = freq;
+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;
}
@@ -272,13 +488,12 @@ 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);
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 0);
+ gpiod_set_value(tas2552->enable_gpio, 0);
return 0;
}
@@ -287,10 +502,9 @@ static int tas2552_runtime_resume(struct device *dev)
{
struct tas2552_data *tas2552 = dev_get_drvdata(dev);
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 1);
+ gpiod_set_value(tas2552->enable_gpio, 1);
- tas2552_sw_shutdown(tas2552, 1);
+ tas2552_sw_shutdown(tas2552, 0);
regcache_cache_only(tas2552->regmap, false);
regcache_sync(tas2552->regmap);
@@ -304,10 +518,12 @@ static const struct dev_pm_ops tas2552_pm = {
NULL)
};
-static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
+static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
.hw_params = tas2552_hw_params,
+ .prepare = tas2552_prepare,
.set_sysclk = tas2552_set_dai_sysclk,
.set_fmt = tas2552_set_dai_fmt,
+ .set_tdm_slot = tas2552_set_dai_tdm_slot,
.digital_mute = tas2552_mute,
};
@@ -333,15 +549,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, -700, 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),
+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)
@@ -360,8 +583,7 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
return ret;
}
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 1);
+ gpiod_set_value(tas2552->enable_gpio, 1);
ret = pm_runtime_get_sync(codec->dev);
if (ret < 0) {
@@ -370,34 +592,22 @@ 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);
+ gpiod_set_value(tas2552->enable_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
@@ -410,8 +620,7 @@ static int tas2552_codec_remove(struct snd_soc_codec *codec)
pm_runtime_put(codec->dev);
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 0);
+ gpiod_set_value(tas2552->enable_gpio, 0);
return 0;
};
@@ -456,6 +665,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,
@@ -487,7 +698,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);
@@ -531,6 +743,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;
}
@@ -551,7 +764,6 @@ MODULE_DEVICE_TABLE(of, tas2552_of_match);
static struct i2c_driver tas2552_i2c_driver = {
.driver = {
.name = "tas2552",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tas2552_of_match),
.pm = &tas2552_pm,
},
diff --git a/kernel/sound/soc/codecs/tas2552.h b/kernel/sound/soc/codecs/tas2552.h
index 6cea8f31b..e34752b8a 100644
--- a/kernel/sound/soc/codecs/tas2552.h
+++ b/kernel/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
+#define TAS2552_MAX_REG TAS2552_VBAT_DATA
/* 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/kernel/sound/soc/codecs/tas5086.c b/kernel/sound/soc/codecs/tas5086.c
index 32942bed3..d49d25d51 100644
--- a/kernel/sound/soc/codecs/tas5086.c
+++ b/kernel/sound/soc/codecs/tas5086.c
@@ -266,10 +266,14 @@ static int tas5086_set_deemph(struct snd_soc_codec *codec)
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
int i, val = 0;
- if (priv->deemph)
- for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++)
- if (tas5086_deemph[i] == priv->rate)
+ if (priv->deemph) {
+ for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++) {
+ if (tas5086_deemph[i] == priv->rate) {
val = i;
+ break;
+ }
+ }
+ }
return regmap_update_bits(priv->regmap, TAS5086_SYS_CONTROL_1,
TAS5086_DEEMPH_MASK, val);
@@ -994,7 +998,6 @@ static int tas5086_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver tas5086_i2c_driver = {
.driver = {
.name = "tas5086",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tas5086_dt_ids),
},
.id_table = tas5086_i2c_id,
diff --git a/kernel/sound/soc/codecs/tas571x.c b/kernel/sound/soc/codecs/tas571x.c
new file mode 100644
index 000000000..39307ad41
--- /dev/null
+++ b/kernel/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 (snd_soc_codec_get_bias_level(codec) == 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/kernel/sound/soc/codecs/tas571x.h b/kernel/sound/soc/codecs/tas571x.h
new file mode 100644
index 000000000..0aee47123
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/codecs/tfa9879.c b/kernel/sound/soc/codecs/tfa9879.c
index aab0af681..cb5310d89 100644
--- a/kernel/sound/soc/codecs/tfa9879.c
+++ b/kernel/sound/soc/codecs/tfa9879.c
@@ -160,7 +160,7 @@ static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static struct reg_default tfa9879_regs[] = {
+static const struct reg_default tfa9879_regs[] = {
{ TFA9879_DEVICE_CONTROL, 0x0000 }, /* 0x00 */
{ TFA9879_SERIAL_INTERFACE_1, 0x0a18 }, /* 0x01 */
{ TFA9879_PCM_IOM2_FORMAT_1, 0x0007 }, /* 0x02 */
@@ -314,7 +314,6 @@ MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id);
static struct i2c_driver tfa9879_i2c_driver = {
.driver = {
.name = "tfa9879",
- .owner = THIS_MODULE,
},
.probe = tfa9879_i2c_probe,
.remove = tfa9879_i2c_remove,
diff --git a/kernel/sound/soc/codecs/tlv320aic23-spi.c b/kernel/sound/soc/codecs/tlv320aic23-spi.c
index 3b387e41d..f801ae051 100644
--- a/kernel/sound/soc/codecs/tlv320aic23-spi.c
+++ b/kernel/sound/soc/codecs/tlv320aic23-spi.c
@@ -43,7 +43,6 @@ static int aic23_spi_remove(struct spi_device *spi)
static struct spi_driver aic23_spi = {
.driver = {
.name = "tlv320aic23",
- .owner = THIS_MODULE,
},
.probe = aic23_spi_probe,
.remove = aic23_spi_remove,
diff --git a/kernel/sound/soc/codecs/tlv320aic23.c b/kernel/sound/soc/codecs/tlv320aic23.c
index cc17e7e51..cd8c02b6e 100644
--- a/kernel/sound/soc/codecs/tlv320aic23.c
+++ b/kernel/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/kernel/sound/soc/codecs/tlv320aic26.c b/kernel/sound/soc/codecs/tlv320aic26.c
index 620ab9ea1..2c904d715 100644
--- a/kernel/sound/soc/codecs/tlv320aic26.c
+++ b/kernel/sound/soc/codecs/tlv320aic26.c
@@ -373,7 +373,6 @@ static int aic26_spi_remove(struct spi_device *spi)
static struct spi_driver aic26_spi = {
.driver = {
.name = "tlv320aic26-codec",
- .owner = THIS_MODULE,
},
.probe = aic26_spi_probe,
.remove = aic26_spi_remove,
diff --git a/kernel/sound/soc/codecs/tlv320aic31xx.c b/kernel/sound/soc/codecs/tlv320aic31xx.c
index c86dd9aae..ee4def4f8 100644
--- a/kernel/sound/soc/codecs/tlv320aic31xx.c
+++ b/kernel/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;
}
@@ -1122,7 +1121,7 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
.num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map),
};
-static struct snd_soc_dai_ops aic31xx_dai_ops = {
+static const struct snd_soc_dai_ops aic31xx_dai_ops = {
.hw_params = aic31xx_hw_params,
.set_sysclk = aic31xx_set_dai_sysclk,
.set_fmt = aic31xx_set_dai_fmt,
@@ -1284,7 +1283,6 @@ MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
static struct i2c_driver aic31xx_i2c_driver = {
.driver = {
.name = "tlv320aic31xx-codec",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
},
.probe = aic31xx_i2c_probe,
diff --git a/kernel/sound/soc/codecs/tlv320aic32x4.c b/kernel/sound/soc/codecs/tlv320aic32x4.c
index 015467ed6..f2d319196 100644
--- a/kernel/sound/soc/codecs/tlv320aic32x4.c
+++ b/kernel/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;
}
@@ -872,7 +871,6 @@ MODULE_DEVICE_TABLE(of, aic32x4_of_id);
static struct i2c_driver aic32x4_i2c_driver = {
.driver = {
.name = "tlv320aic32x4",
- .owner = THIS_MODULE,
.of_match_table = aic32x4_of_id,
},
.probe = aic32x4_i2c_probe,
diff --git a/kernel/sound/soc/codecs/tlv320aic3x.c b/kernel/sound/soc/codecs/tlv320aic3x.c
index 51c4713ac..a56475984 100644
--- a/kernel/sound/soc/codecs/tlv320aic3x.c
+++ b/kernel/sound/soc/codecs/tlv320aic3x.c
@@ -80,6 +80,7 @@ struct aic3x_priv {
unsigned int sysclk;
unsigned int dai_fmt;
unsigned int tdm_delay;
+ unsigned int slot_width;
struct list_head list;
int master;
int gpio_reset;
@@ -147,6 +148,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 +181,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 +981,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:
@@ -1024,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
int clk;
+ int width = aic3x->slot_width;
+
+ if (!width)
+ width = params_width(params);
/* select data word length */
data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
- switch (params_width(params)) {
+ switch (width) {
case 16:
break;
case 20:
@@ -1169,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int delay = 0;
+ int width = aic3x->slot_width;
+
+ if (!width)
+ width = substream->runtime->sample_bits;
/* TDM slot selection only valid in DSP_A/_B mode */
if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
- delay += (aic3x->tdm_delay + 1);
+ delay += (aic3x->tdm_delay*width + 1);
else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
- delay += aic3x->tdm_delay;
+ delay += aic3x->tdm_delay*width;
/* Configure data delay */
snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
@@ -1295,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- aic3x->tdm_delay = lsb * slot_width;
+ switch (slot_width) {
+ case 16:
+ case 20:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+
+ aic3x->tdm_delay = lsb;
+ aic3x->slot_width = slot_width;
/* DOUT in high-impedance on inactive bit clocks */
snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
@@ -1384,7 +1407,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 +1417,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 +1429,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;
}
@@ -1509,14 +1531,17 @@ static int aic3x_init(struct snd_soc_codec *codec)
snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
- /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
- /* Line2 Line Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+ /* On tlv320aic3104, these registers are reserved and must not be written */
+ if (aic3x->model != AIC3X_MODEL_3104) {
+ /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
+ /* Line2 Line Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+ }
switch (aic3x->model) {
case AIC3X_MODEL_3X:
@@ -1668,7 +1693,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
-static const struct reg_default aic3007_class_d[] = {
+static const struct reg_sequence aic3007_class_d[] = {
/* Class-D speaker driver init; datasheet p. 46 */
{ AIC3X_PAGE_SELECT, 0x0D },
{ 0xD, 0x0D },
@@ -1825,7 +1850,6 @@ MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match);
static struct i2c_driver aic3x_i2c_driver = {
.driver = {
.name = "tlv320aic3x-codec",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tlv320aic3x_of_match),
},
.probe = aic3x_i2c_probe,
diff --git a/kernel/sound/soc/codecs/tlv320dac33.c b/kernel/sound/soc/codecs/tlv320dac33.c
index 4e3e607de..781398fb2 100644
--- a/kernel/sound/soc/codecs/tlv320dac33.c
+++ b/kernel/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;
}
@@ -1586,7 +1585,6 @@ MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
static struct i2c_driver tlv320dac33_i2c_driver = {
.driver = {
.name = "tlv320dac33-codec",
- .owner = THIS_MODULE,
},
.probe = dac33_i2c_probe,
.remove = dac33_i2c_remove,
diff --git a/kernel/sound/soc/codecs/tpa6130a2.c b/kernel/sound/soc/codecs/tpa6130a2.c
index 6fac9e034..11d85c5c7 100644
--- a/kernel/sound/soc/codecs/tpa6130a2.c
+++ b/kernel/sound/soc/codecs/tpa6130a2.c
@@ -259,8 +259,7 @@ static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol,
* TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
* down in gain.
*/
-static const unsigned int tpa6130_tlv[] = {
- TLV_DB_RANGE_HEAD(10),
+static const DECLARE_TLV_DB_RANGE(tpa6130_tlv,
0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
@@ -270,8 +269,8 @@ static const unsigned int tpa6130_tlv[] = {
12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
- 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
-};
+ 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0)
+);
static const struct snd_kcontrol_new tpa6130a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
@@ -280,12 +279,11 @@ static const struct snd_kcontrol_new tpa6130a2_controls[] = {
tpa6130_tlv),
};
-static const unsigned int tpa6140_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(tpa6140_tlv,
0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0),
9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0),
- 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0),
-};
+ 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0)
+);
static const struct snd_kcontrol_new tpa6140a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
@@ -488,7 +486,6 @@ MODULE_DEVICE_TABLE(of, tpa6130a2_of_match);
static struct i2c_driver tpa6130a2_i2c_driver = {
.driver = {
.name = "tpa6130a2",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tpa6130a2_of_match),
},
.probe = tpa6130a2_probe,
diff --git a/kernel/sound/soc/codecs/ts3a227e.c b/kernel/sound/soc/codecs/ts3a227e.c
index 9fd80ac18..43568435c 100644
--- a/kernel/sound/soc/codecs/ts3a227e.c
+++ b/kernel/sound/soc/codecs/ts3a227e.c
@@ -23,11 +23,13 @@
#include "ts3a227e.h"
struct ts3a227e {
+ struct device *dev;
struct regmap *regmap;
struct snd_soc_jack *jack;
bool plugged;
bool mic_present;
unsigned int buttons_held;
+ int irq;
};
/* Button values to be reported on the jack */
@@ -189,16 +191,28 @@ static irqreturn_t ts3a227e_interrupt(int irq, void *data)
struct ts3a227e *ts3a227e = (struct ts3a227e *)data;
struct regmap *regmap = ts3a227e->regmap;
unsigned int int_reg, kp_int_reg, acc_reg, i;
+ struct device *dev = ts3a227e->dev;
+ int ret;
/* Check for plug/unplug. */
- regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg);
+ ret = regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg);
+ if (ret) {
+ dev_err(dev, "failed to clear interrupt ret=%d\n", ret);
+ return IRQ_NONE;
+ }
+
if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) {
regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg);
ts3a227e_new_jack_state(ts3a227e, acc_reg);
}
/* Report any key events. */
- regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg);
+ ret = regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg);
+ if (ret) {
+ dev_err(dev, "failed to clear key interrupt ret=%d\n", ret);
+ return IRQ_NONE;
+ }
+
for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
if (kp_int_reg & PRESS_MASK(i))
ts3a227e->buttons_held |= (1 << i);
@@ -254,12 +268,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,
@@ -282,17 +297,17 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, ts3a227e);
+ ts3a227e->dev = dev;
+ ts3a227e->irq = i2c->irq;
ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config);
if (IS_ERR(ts3a227e->regmap))
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,
@@ -321,6 +336,32 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c,
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int ts3a227e_suspend(struct device *dev)
+{
+ struct ts3a227e *ts3a227e = dev_get_drvdata(dev);
+
+ dev_dbg(ts3a227e->dev, "suspend disable irq\n");
+ disable_irq(ts3a227e->irq);
+
+ return 0;
+}
+
+static int ts3a227e_resume(struct device *dev)
+{
+ struct ts3a227e *ts3a227e = dev_get_drvdata(dev);
+
+ dev_dbg(ts3a227e->dev, "resume enable irq\n");
+ enable_irq(ts3a227e->irq);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops ts3a227e_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume)
+};
+
static const struct i2c_device_id ts3a227e_i2c_ids[] = {
{ "ts3a227e", 0 },
{ }
@@ -336,7 +377,7 @@ MODULE_DEVICE_TABLE(of, ts3a227e_of_match);
static struct i2c_driver ts3a227e_driver = {
.driver = {
.name = "ts3a227e",
- .owner = THIS_MODULE,
+ .pm = &ts3a227e_pm,
.of_match_table = of_match_ptr(ts3a227e_of_match),
},
.probe = ts3a227e_i2c_probe,
diff --git a/kernel/sound/soc/codecs/twl4030.c b/kernel/sound/soc/codecs/twl4030.c
index d04693e9c..a5a4e9f75 100644
--- a/kernel/sound/soc/codecs/twl4030.c
+++ b/kernel/sound/soc/codecs/twl4030.c
@@ -524,12 +524,11 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
/* Digital bypass gain, mute instead of -30dB */
-static const unsigned int twl4030_dapm_dbypass_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(twl4030_dapm_dbypass_tlv,
0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1),
2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0),
- 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0),
-};
+ 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0)
+);
/* Digital bypass left (TX1L -> RX2L) */
static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control =
@@ -1588,14 +1587,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;
}
@@ -1614,19 +1612,16 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
return;
/* Set the constraints according to the already configured stream */
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- twl4030->rate,
twl4030->rate);
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- twl4030->sample_bits,
twl4030->sample_bits);
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- twl4030->channels,
twl4030->channels);
}
@@ -1671,9 +1666,9 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
/* In option2 4 channel is not supported, set the
* constraint for the first stream for channels, the
* second stream will 'inherit' this cosntraint */
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- 2, 2);
+ 2);
}
twl4030->master_substream = substream;
}
diff --git a/kernel/sound/soc/codecs/twl6040.c b/kernel/sound/soc/codecs/twl6040.c
index aeec27b6f..4cad8929d 100644
--- a/kernel/sound/soc/codecs/twl6040.c
+++ b/kernel/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/kernel/sound/soc/codecs/uda134x.c b/kernel/sound/soc/codecs/uda134x.c
index f883308c0..e4c694c75 100644
--- a/kernel/sound/soc/codecs/uda134x.c
+++ b/kernel/sound/soc/codecs/uda134x.c
@@ -37,74 +37,53 @@ struct uda134x_priv {
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
-};
-/* In-data addresses are hard-coded into the reg-cache values */
-static const char uda134x_reg[UDA134X_REGS_NUM] = {
- /* Extended address registers */
- 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Status, data regs */
- 0x00, 0x83, 0x00, 0x40, 0x80, 0xC0, 0x00,
+ struct regmap *regmap;
+ struct uda134x_platform_data *pd;
};
-/*
- * The codec has no support for reading its registers except for peak level...
- */
-static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u8 *cache = codec->reg_cache;
-
- if (reg >= UDA134X_REGS_NUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * Write the register cache
- */
-static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
- u8 reg, unsigned int value)
-{
- u8 *cache = codec->reg_cache;
-
- if (reg >= UDA134X_REGS_NUM)
- return;
- cache[reg] = value;
-}
+static const struct reg_default uda134x_reg_defaults[] = {
+ { UDA134X_EA000, 0x04 },
+ { UDA134X_EA001, 0x04 },
+ { UDA134X_EA010, 0x04 },
+ { UDA134X_EA011, 0x00 },
+ { UDA134X_EA100, 0x00 },
+ { UDA134X_EA101, 0x00 },
+ { UDA134X_EA110, 0x00 },
+ { UDA134X_EA111, 0x00 },
+ { UDA134X_STATUS0, 0x00 },
+ { UDA134X_STATUS1, 0x03 },
+ { UDA134X_DATA000, 0x00 },
+ { UDA134X_DATA001, 0x00 },
+ { UDA134X_DATA010, 0x00 },
+ { UDA134X_DATA011, 0x00 },
+ { UDA134X_DATA1, 0x00 },
+};
/*
* Write to the uda134x registers
*
*/
-static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
+static int uda134x_regmap_write(void *context, unsigned int reg,
unsigned int value)
{
+ struct uda134x_platform_data *pd = context;
int ret;
u8 addr;
u8 data = value;
- struct uda134x_platform_data *pd = codec->control_data;
-
- pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
-
- if (reg >= UDA134X_REGS_NUM) {
- printk(KERN_ERR "%s unknown register: reg: %u",
- __func__, reg);
- return -EINVAL;
- }
-
- uda134x_write_reg_cache(codec, reg, value);
switch (reg) {
case UDA134X_STATUS0:
case UDA134X_STATUS1:
addr = UDA134X_STATUS_ADDR;
+ data |= (reg - UDA134X_STATUS0) << 7;
break;
case UDA134X_DATA000:
case UDA134X_DATA001:
case UDA134X_DATA010:
case UDA134X_DATA011:
addr = UDA134X_DATA0_ADDR;
+ data |= (reg - UDA134X_DATA000) << 6;
break;
case UDA134X_DATA1:
addr = UDA134X_DATA1_ADDR;
@@ -133,27 +112,28 @@ static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
static inline void uda134x_reset(struct snd_soc_codec *codec)
{
- u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
- uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+ unsigned int mask = 1<<6;
+
+ regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask);
msleep(1);
- uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
+ regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0);
}
static int uda134x_mute(struct snd_soc_dai *dai, int mute)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int mask = 1<<2;
+ unsigned int val;
pr_debug("%s mute: %d\n", __func__, mute);
if (mute)
- mute_reg |= (1<<2);
+ val = mask;
else
- mute_reg &= ~(1<<2);
+ val = 0;
- uda134x_write(codec, UDA134X_DATA010, mute_reg);
-
- return 0;
+ return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val);
}
static int uda134x_startup(struct snd_pcm_substream *substream,
@@ -170,14 +150,12 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
master_runtime->sample_bits,
master_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- master_runtime->rate,
master_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- master_runtime->sample_bits,
master_runtime->sample_bits);
uda134x->slave_substream = substream;
@@ -205,7 +183,7 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
- u8 hw_params;
+ unsigned int hw_params = 0;
if (substream == uda134x->slave_substream) {
pr_debug("%s ignoring hw_params for slave substream\n",
@@ -213,10 +191,6 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
return 0;
}
- hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
- hw_params &= STATUS0_SYSCLK_MASK;
- hw_params &= STATUS0_DAIFMT_MASK;
-
pr_debug("%s sysclk: %d, rate:%d\n", __func__,
uda134x->sysclk, params_rate(params));
@@ -267,9 +241,8 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- uda134x_write(codec, UDA134X_STATUS0, hw_params);
-
- return 0;
+ return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0,
+ STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params);
}
static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
@@ -324,10 +297,8 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int uda134x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct uda134x_platform_data *pd = codec->control_data;
- int i;
- u8 *cache = codec->reg_cache;
-
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+ struct uda134x_platform_data *pd = uda134x->pd;
pr_debug("%s bias level %d\n", __func__, level);
switch (level) {
@@ -337,20 +308,19 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
/* power on */
if (pd->power) {
pd->power(1);
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
- codec->driver->write(codec, i, *cache++);
+ regcache_sync(uda134x->regmap);
}
break;
case SND_SOC_BIAS_STANDBY:
break;
case SND_SOC_BIAS_OFF:
/* power off */
- if (pd->power)
+ if (pd->power) {
pd->power(0);
+ regcache_mark_dirty(uda134x->regmap);
+ }
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -478,21 +448,15 @@ static struct snd_soc_dai_driver uda134x_dai = {
static int uda134x_soc_probe(struct snd_soc_codec *codec)
{
- struct uda134x_priv *uda134x;
- struct uda134x_platform_data *pd = codec->component.card->dev->platform_data;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+ struct uda134x_platform_data *pd = uda134x->pd;
const struct snd_soc_dapm_widget *widgets;
unsigned num_widgets;
-
int ret;
printk(KERN_INFO "UDA134X SoC Audio Codec\n");
- if (!pd) {
- printk(KERN_ERR "UDA134X SoC codec: "
- "missing L3 bitbang function\n");
- return -ENODEV;
- }
-
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1341:
@@ -506,13 +470,6 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
return -EINVAL;
}
- uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
- if (uda134x == NULL)
- return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, uda134x);
-
- codec->control_data = pd;
-
if (pd->power)
pd->power(1);
@@ -526,11 +483,10 @@ 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);
- kfree(uda134x);
return ret;
}
@@ -551,36 +507,19 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
default:
printk(KERN_ERR "%s unknown codec type: %d",
__func__, pd->model);
- kfree(uda134x);
return -EINVAL;
}
if (ret < 0) {
printk(KERN_ERR "UDA134X: failed to register controls\n");
- kfree(uda134x);
return ret;
}
return 0;
}
-/* power down chip */
-static int uda134x_soc_remove(struct snd_soc_codec *codec)
-{
- struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
-
- kfree(uda134x);
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.probe = uda134x_soc_probe,
- .remove = uda134x_soc_remove,
- .reg_cache_size = sizeof(uda134x_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = uda134x_reg,
- .reg_cache_step = 1,
- .read = uda134x_read_reg_cache,
.set_bias_level = uda134x_set_bias_level,
.suspend_bias_off = true,
@@ -590,8 +529,39 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
};
+static const struct regmap_config uda134x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = UDA134X_DATA1,
+ .reg_defaults = uda134x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .reg_write = uda134x_regmap_write,
+};
+
static int uda134x_codec_probe(struct platform_device *pdev)
{
+ struct uda134x_platform_data *pd = pdev->dev.platform_data;
+ struct uda134x_priv *uda134x;
+
+ if (!pd) {
+ dev_err(&pdev->dev, "Missing L3 bitbang function\n");
+ return -ENODEV;
+ }
+
+ uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL);
+ if (!uda134x)
+ return -ENOMEM;
+
+ uda134x->pd = pd;
+ platform_set_drvdata(pdev, uda134x);
+
+ uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,
+ &uda134x_regmap_config);
+ if (IS_ERR(uda134x->regmap))
+ return PTR_ERR(uda134x->regmap);
+
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_uda134x, &uda134x_dai, 1);
}
diff --git a/kernel/sound/soc/codecs/uda134x.h b/kernel/sound/soc/codecs/uda134x.h
index 9faae0697..e41ab38c6 100644
--- a/kernel/sound/soc/codecs/uda134x.h
+++ b/kernel/sound/soc/codecs/uda134x.h
@@ -26,8 +26,6 @@
#define UDA134X_DATA011 13
#define UDA134X_DATA1 14
-#define UDA134X_REGS_NUM 15
-
#define STATUS0_DAIFMT_MASK (~(7<<1))
#define STATUS0_SYSCLK_MASK (~(3<<4))
diff --git a/kernel/sound/soc/codecs/uda1380.c b/kernel/sound/soc/codecs/uda1380.c
index c3c33bd0d..35f0469eb 100644
--- a/kernel/sound/soc/codecs/uda1380.c
+++ b/kernel/sound/soc/codecs/uda1380.c
@@ -269,12 +269,11 @@ static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
* from -66 dB in 0.5 dB steps (2 dB steps, really) and
* from -52 dB in 0.25 dB steps
*/
-static const unsigned int mvol_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(mvol_tlv,
0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
- 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
-};
+ 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0)
+);
/*
* from -72 dB in 1.5 dB steps (6 dB steps really),
@@ -282,13 +281,12 @@ static const unsigned int mvol_tlv[] = {
* from -60 dB in 0.5 dB steps (2 dB steps really) and
* from -46 dB in 0.25 dB steps
*/
-static const unsigned int vc_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(vc_tlv,
0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
- 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
-};
+ 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0)
+);
/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
@@ -590,9 +588,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 +595,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 +618,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;
}
@@ -814,7 +808,6 @@ MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
static struct i2c_driver uda1380_i2c_driver = {
.driver = {
.name = "uda1380-codec",
- .owner = THIS_MODULE,
},
.probe = uda1380_i2c_probe,
.remove = uda1380_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wl1273.c b/kernel/sound/soc/codecs/wl1273.c
index 80fb1dc81..7693c1129 100644
--- a/kernel/sound/soc/codecs/wl1273.c
+++ b/kernel/sound/soc/codecs/wl1273.c
@@ -307,11 +307,10 @@ static int wl1273_startup(struct snd_pcm_substream *substream,
switch (wl1273->mode) {
case WL1273_MODE_BT:
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- 8000, 8000);
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 8000);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 1);
break;
case WL1273_MODE_FM_RX:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff --git a/kernel/sound/soc/codecs/wm0010.c b/kernel/sound/soc/codecs/wm0010.c
index f37989ec7..e3c34bdc2 100644
--- a/kernel/sound/soc/codecs/wm0010.c
+++ b/kernel/sound/soc/codecs/wm0010.c
@@ -577,7 +577,6 @@ static int wm0010_boot(struct snd_soc_codec *codec)
struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
unsigned long flags;
int ret;
- const struct firmware *fw;
struct spi_message m;
struct spi_transfer t;
struct dfw_pllrec pll_rec;
@@ -623,14 +622,6 @@ static int wm0010_boot(struct snd_soc_codec *codec)
wm0010->state = WM0010_OUT_OF_RESET;
spin_unlock_irqrestore(&wm0010->irq_lock, flags);
- /* First the bootloader */
- ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request stage2 loader: %d\n",
- ret);
- goto abort;
- }
-
if (!wait_for_completion_timeout(&wm0010->boot_completion,
msecs_to_jiffies(20)))
dev_err(codec->dev, "Failed to get interrupt from DSP\n");
@@ -673,7 +664,7 @@ static int wm0010_boot(struct snd_soc_codec *codec)
img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img_swap)
- goto abort;
+ goto abort_out;
/* We need to re-order for 0010 */
byte_swap_64((u64 *)&pll_rec, img_swap, len);
@@ -688,16 +679,16 @@ static int wm0010_boot(struct snd_soc_codec *codec)
spi_message_add_tail(&t, &m);
ret = spi_sync(spi, &m);
- if (ret != 0) {
+ if (ret) {
dev_err(codec->dev, "First PLL write failed: %d\n", ret);
- goto abort;
+ goto abort_swap;
}
/* Use a second send of the message to get the return status */
ret = spi_sync(spi, &m);
- if (ret != 0) {
+ if (ret) {
dev_err(codec->dev, "Second PLL write failed: %d\n", ret);
- goto abort;
+ goto abort_swap;
}
p = (u32 *)out;
@@ -730,6 +721,10 @@ static int wm0010_boot(struct snd_soc_codec *codec)
return 0;
+abort_swap:
+ kfree(img_swap);
+abort_out:
+ kfree(out);
abort:
/* Put the chip back into reset */
wm0010_halt(codec);
@@ -751,13 +746,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 +762,6 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -955,7 +948,7 @@ static int wm0010_spi_probe(struct spi_device *spi)
trigger = IRQF_TRIGGER_FALLING;
trigger |= IRQF_ONESHOT;
- ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger | IRQF_ONESHOT,
+ ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger,
"wm0010", wm0010);
if (ret) {
dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n",
@@ -1005,8 +998,6 @@ static int wm0010_spi_remove(struct spi_device *spi)
static struct spi_driver wm0010_spi_driver = {
.driver = {
.name = "wm0010",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
},
.probe = wm0010_spi_probe,
.remove = wm0010_spi_remove,
diff --git a/kernel/sound/soc/codecs/wm1250-ev1.c b/kernel/sound/soc/codecs/wm1250-ev1.c
index 8011f75fb..ec45c5b22 100644
--- a/kernel/sound/soc/codecs/wm1250-ev1.c
+++ b/kernel/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;
}
@@ -253,7 +251,6 @@ MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
static struct i2c_driver wm1250_ev1_i2c_driver = {
.driver = {
.name = "wm1250-ev1",
- .owner = THIS_MODULE,
},
.probe = wm1250_ev1_probe,
.remove = wm1250_ev1_remove,
diff --git a/kernel/sound/soc/codecs/wm2000.c b/kernel/sound/soc/codecs/wm2000.c
index 21d5402e3..a67ea10f4 100644
--- a/kernel/sound/soc/codecs/wm2000.c
+++ b/kernel/sound/soc/codecs/wm2000.c
@@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- int anc_active = ucontrol->value.integer.value[0];
+ unsigned int anc_active = ucontrol->value.integer.value[0];
int ret;
if (anc_active > 1)
@@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- int val = ucontrol->value.integer.value[0];
+ unsigned int val = ucontrol->value.integer.value[0];
int ret;
if (val > 1)
@@ -942,7 +942,6 @@ MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id);
static struct i2c_driver wm2000_i2c_driver = {
.driver = {
.name = "wm2000",
- .owner = THIS_MODULE,
},
.probe = wm2000_i2c_probe,
.remove = wm2000_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm2200.c b/kernel/sound/soc/codecs/wm2200.c
index 5a9da28f4..fd1439ecb 100644
--- a/kernel/sound/soc/codecs/wm2200.c
+++ b/kernel/sound/soc/codecs/wm2200.c
@@ -166,7 +166,7 @@ static const struct wm_adsp_region wm2200_dsp2_regions[] = {
{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE },
};
-static struct reg_default wm2200_reg_defaults[] = {
+static const struct reg_default wm2200_reg_defaults[] = {
{ 0x000B, 0x0000 }, /* R11 - Tone Generator 1 */
{ 0x0102, 0x0000 }, /* R258 - Clocking 3 */
{ 0x0103, 0x0011 }, /* R259 - Clocking 4 */
@@ -897,7 +897,7 @@ static bool wm2200_readable_register(struct device *dev, unsigned int reg)
}
}
-static const struct reg_default wm2200_reva_patch[] = {
+static const struct reg_sequence wm2200_reva_patch[] = {
{ 0x07, 0x0003 },
{ 0x102, 0x0200 },
{ 0x203, 0x0084 },
@@ -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;
@@ -1702,7 +1702,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
int *bclk_rates;
/* Data sizes if not using TDM */
- wl = snd_pcm_format_width(params_format(params));
+ wl = params_width(params);
if (wl < 0)
return wl;
fl = snd_soc_params_to_frame_size(params);
@@ -2481,7 +2481,7 @@ static int wm2200_runtime_resume(struct device *dev)
}
#endif
-static struct dev_pm_ops wm2200_pm = {
+static const struct dev_pm_ops wm2200_pm = {
SET_RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume,
NULL)
};
@@ -2495,7 +2495,6 @@ MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id);
static struct i2c_driver wm2200_i2c_driver = {
.driver = {
.name = "wm2200",
- .owner = THIS_MODULE,
.pm = &wm2200_pm,
},
.probe = wm2200_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm5100.c b/kernel/sound/soc/codecs/wm5100.c
index 96740379b..c2cdcae18 100644
--- a/kernel/sound/soc/codecs/wm5100.c
+++ b/kernel/sound/soc/codecs/wm5100.c
@@ -1247,7 +1247,7 @@ static const struct snd_soc_dapm_route wm5100_dapm_routes[] = {
{ "PWM2", NULL, "PWM2 Driver" },
};
-static const struct reg_default wm5100_reva_patches[] = {
+static const struct reg_sequence wm5100_reva_patches[] = {
{ WM5100_AUDIO_IF_1_10, 0 },
{ WM5100_AUDIO_IF_1_11, 1 },
{ WM5100_AUDIO_IF_1_12, 2 },
@@ -1408,7 +1408,7 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
base = dai->driver->base;
/* Data sizes if not using TDM */
- wl = snd_pcm_format_width(params_format(params));
+ wl = params_width(params);
if (wl < 0)
return wl;
fl = snd_soc_params_to_frame_size(params);
@@ -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) {
@@ -2706,7 +2706,7 @@ static int wm5100_runtime_resume(struct device *dev)
}
#endif
-static struct dev_pm_ops wm5100_pm = {
+static const struct dev_pm_ops wm5100_pm = {
SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume,
NULL)
};
@@ -2720,7 +2720,6 @@ MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id);
static struct i2c_driver wm5100_i2c_driver = {
.driver = {
.name = "wm5100",
- .owner = THIS_MODULE,
.pm = &wm5100_pm,
},
.probe = wm5100_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm5102.c b/kernel/sound/soc/codecs/wm5102.c
index d476221db..64637d1cf 100644
--- a/kernel/sound/soc/codecs/wm5102.c
+++ b/kernel/sound/soc/codecs/wm5102.c
@@ -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,
@@ -744,8 +788,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
-SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
-SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2),
SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
@@ -757,8 +800,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
-SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2),
SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
@@ -770,8 +812,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
-SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2),
SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
@@ -783,8 +824,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
-SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2),
SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
@@ -807,10 +847,10 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
-SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
-SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
-SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
-SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2),
+ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
+ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
+ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
@@ -1036,7 +1076,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 +1408,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,27 +1868,40 @@ 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)
+ goto err_adsp2_codec_probe;
+
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;
+
+err_adsp2_codec_probe:
+ wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
+
+ return ret;
}
static int wm5102_codec_remove(struct snd_soc_codec *codec)
{
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 +1963,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 +1974,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/kernel/sound/soc/codecs/wm5110.c b/kernel/sound/soc/codecs/wm5110.c
index 3ee6cfd05..52b9ccf6d 100644
--- a/kernel/sound/soc/codecs/wm5110.c
+++ b/kernel/sound/soc/codecs/wm5110.c
@@ -38,6 +38,12 @@
struct wm5110_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
+
+ unsigned int in_value;
+ int in_pre_pending;
+ int in_post_pending;
+
+ unsigned int in_pga_cache[6];
};
static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -131,6 +137,25 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = {
{ 0x33fb, 0xfe00 },
};
+static const struct reg_default wm5110_sysclk_reve_patch[] = {
+ { 0x3270, 0xE410 },
+ { 0x3271, 0x3078 },
+ { 0x3272, 0xE410 },
+ { 0x3273, 0x3070 },
+ { 0x3274, 0xE410 },
+ { 0x3275, 0x3066 },
+ { 0x3276, 0xE410 },
+ { 0x3277, 0x3056 },
+ { 0x327A, 0xE414 },
+ { 0x327B, 0x3078 },
+ { 0x327C, 0xE414 },
+ { 0x327D, 0x3070 },
+ { 0x327E, 0xE414 },
+ { 0x327F, 0x3066 },
+ { 0x3280, 0xE414 },
+ { 0x3281, 0x3056 },
+};
+
static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -146,7 +171,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch);
break;
default:
- return 0;
+ patch = wm5110_sysclk_reve_patch;
+ patch_size = ARRAY_SIZE(wm5110_sysclk_reve_patch);
+ break;
}
switch (event) {
@@ -164,6 +191,368 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
return 0;
}
+static const struct reg_sequence wm5110_no_dre_left_enable[] = {
+ { 0x3024, 0xE410 },
+ { 0x3025, 0x0056 },
+ { 0x301B, 0x0224 },
+ { 0x301F, 0x4263 },
+ { 0x3021, 0x5291 },
+ { 0x3030, 0xE410 },
+ { 0x3031, 0x3066 },
+ { 0x3032, 0xE410 },
+ { 0x3033, 0x3070 },
+ { 0x3034, 0xE410 },
+ { 0x3035, 0x3078 },
+ { 0x3036, 0xE410 },
+ { 0x3037, 0x3080 },
+ { 0x3038, 0xE410 },
+ { 0x3039, 0x3080 },
+};
+
+static const struct reg_sequence wm5110_dre_left_enable[] = {
+ { 0x3024, 0x0231 },
+ { 0x3025, 0x0B00 },
+ { 0x301B, 0x0227 },
+ { 0x301F, 0x4266 },
+ { 0x3021, 0x5294 },
+ { 0x3030, 0xE231 },
+ { 0x3031, 0x0266 },
+ { 0x3032, 0x8231 },
+ { 0x3033, 0x4B15 },
+ { 0x3034, 0x8231 },
+ { 0x3035, 0x0B15 },
+ { 0x3036, 0xE231 },
+ { 0x3037, 0x5294 },
+ { 0x3038, 0x0231 },
+ { 0x3039, 0x0B00 },
+};
+
+static const struct reg_sequence wm5110_no_dre_right_enable[] = {
+ { 0x3074, 0xE414 },
+ { 0x3075, 0x0056 },
+ { 0x306B, 0x0224 },
+ { 0x306F, 0x4263 },
+ { 0x3071, 0x5291 },
+ { 0x3080, 0xE414 },
+ { 0x3081, 0x3066 },
+ { 0x3082, 0xE414 },
+ { 0x3083, 0x3070 },
+ { 0x3084, 0xE414 },
+ { 0x3085, 0x3078 },
+ { 0x3086, 0xE414 },
+ { 0x3087, 0x3080 },
+ { 0x3088, 0xE414 },
+ { 0x3089, 0x3080 },
+};
+
+static const struct reg_sequence wm5110_dre_right_enable[] = {
+ { 0x3074, 0x0231 },
+ { 0x3075, 0x0B00 },
+ { 0x306B, 0x0227 },
+ { 0x306F, 0x4266 },
+ { 0x3071, 0x5294 },
+ { 0x3080, 0xE231 },
+ { 0x3081, 0x0266 },
+ { 0x3082, 0x8231 },
+ { 0x3083, 0x4B17 },
+ { 0x3084, 0x8231 },
+ { 0x3085, 0x0B17 },
+ { 0x3086, 0xE231 },
+ { 0x3087, 0x5294 },
+ { 0x3088, 0x0231 },
+ { 0x3089, 0x0B00 },
+};
+
+static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE);
+ const struct reg_sequence *wseq;
+ int nregs;
+
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ if (val & ARIZONA_DRE1L_ENA_MASK) {
+ wseq = wm5110_dre_left_enable;
+ nregs = ARRAY_SIZE(wm5110_dre_left_enable);
+ } else {
+ wseq = wm5110_no_dre_left_enable;
+ nregs = ARRAY_SIZE(wm5110_no_dre_left_enable);
+ priv->out_up_delay += 10;
+ }
+ break;
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ if (val & ARIZONA_DRE1R_ENA_MASK) {
+ wseq = wm5110_dre_right_enable;
+ nregs = ARRAY_SIZE(wm5110_dre_right_enable);
+ } else {
+ wseq = wm5110_no_dre_right_enable;
+ nregs = ARRAY_SIZE(wm5110_no_dre_right_enable);
+ priv->out_up_delay += 10;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ return regmap_multi_reg_write(arizona->regmap, wseq, nregs);
+}
+
+static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE);
+
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ if (!(val & ARIZONA_DRE1L_ENA_MASK)) {
+ snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG1, ARIZONA_WS_TRG1);
+ snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG1, 0);
+ priv->out_down_delay += 27;
+ }
+ break;
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ if (!(val & ARIZONA_DRE1R_ENA_MASK)) {
+ snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG2, ARIZONA_WS_TRG2);
+ snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG2, 0);
+ priv->out_down_delay += 27;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wm5110_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (priv->arizona->rev) {
+ case 0 ... 3:
+ break;
+ default:
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wm5110_hp_pre_enable(w);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wm5110_hp_pre_disable(w);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return arizona_hp_ev(w, kcontrol, event);
+}
+
+static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
+{
+ unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
+ int ret;
+
+ ret = regmap_write(arizona->regmap, reg, 0x80);
+ if (ret)
+ dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+static int wm5110_put_dre(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int ena, dre;
+ unsigned int mask = (0x1 << mc->shift) | (0x1 << mc->rshift);
+ unsigned int lnew = (!!ucontrol->value.integer.value[0]) << mc->shift;
+ unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift;
+ unsigned int lold, rold;
+ unsigned int lena, rena;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &ena);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read output state: %d\n", ret);
+ goto err;
+ }
+ ret = regmap_read(arizona->regmap, ARIZONA_DRE_ENABLE, &dre);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read DRE state: %d\n", ret);
+ goto err;
+ }
+
+ lold = dre & (1 << mc->shift);
+ rold = dre & (1 << mc->rshift);
+ /* Enables are channel wise swapped from the DRE enables */
+ lena = ena & (1 << mc->rshift);
+ rena = ena & (1 << mc->shift);
+
+ if ((lena && lnew != lold) || (rena && rnew != rold)) {
+ dev_err(arizona->dev, "Can't change DRE on active outputs\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_DRE_ENABLE,
+ mask, lnew | rnew);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to set DRE: %d\n", ret);
+ goto err;
+ }
+
+ /* Force reset of PGA volumes, if turning DRE off */
+ if (!lnew && lold)
+ wm5110_clear_pga_volume(arizona, mc->shift);
+
+ if (!rnew && rold)
+ wm5110_clear_pga_volume(arizona, mc->rshift);
+
+err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int wm5110_in_pga_get(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 snd_soc_card *card = dapm->card;
+ int ret;
+
+ /*
+ * PGA Volume is also used as part of the enable sequence, so
+ * usage of it should be avoided whilst that is running.
+ */
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+ mutex_unlock(&card->dapm_mutex);
+
+ return ret;
+}
+
+static int wm5110_in_pga_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 snd_soc_card *card = dapm->card;
+ int ret;
+
+ /*
+ * PGA Volume is also used as part of the enable sequence, so
+ * usage of it should be avoided whilst that is running.
+ */
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+ mutex_unlock(&card->dapm_mutex);
+
+ return ret;
+}
+
+static int wm5110_in_analog_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);
+ struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ unsigned int reg, mask;
+ struct reg_sequence analog_seq[] = {
+ { 0x80, 0x3 },
+ { 0x35d, 0 },
+ { 0x80, 0x0 },
+ };
+
+ reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+ mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+ switch (event) {
+ case SND_SOC_DAPM_WILL_PMU:
+ wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+ wm5110->in_pre_pending++;
+ wm5110->in_post_pending++;
+ return 0;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+ snd_soc_update_bits(codec, reg, mask,
+ 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+ wm5110->in_pre_pending--;
+ if (wm5110->in_pre_pending == 0) {
+ analog_seq[1].def = wm5110->in_value;
+ regmap_multi_reg_write_bypassed(arizona->regmap,
+ analog_seq,
+ ARRAY_SIZE(analog_seq));
+
+ msleep(55);
+
+ wm5110->in_value = 0;
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, reg, mask,
+ wm5110->in_pga_cache[w->shift]);
+
+ wm5110->in_post_pending--;
+ if (wm5110->in_post_pending == 0)
+ regmap_multi_reg_write_bypassed(arizona->regmap,
+ analog_seq,
+ ARRAY_SIZE(analog_seq));
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wm5110_in_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);
+ struct arizona *arizona = priv->arizona;
+
+ switch (arizona->rev) {
+ case 0 ... 4:
+ if (arizona_input_analog(codec, w->shift))
+ wm5110_in_analog_ev(w, kcontrol, event);
+
+ break;
+ default:
+ break;
+ }
+
+ return arizona_in_ev(w, kcontrol, event);
+}
+
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);
@@ -190,18 +579,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
-SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
- ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
- ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
- ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
@@ -247,8 +642,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
-SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
-SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2),
SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
@@ -260,8 +654,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
-SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2),
SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
@@ -273,8 +666,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
-SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2),
SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
@@ -286,8 +678,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
-SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2),
SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
@@ -314,10 +705,10 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
-SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
-SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
-SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
-SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2),
+ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
+ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
+ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
@@ -409,12 +800,15 @@ SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
ARIZONA_SPK2R_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
- ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
-SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
- ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
-SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE,
- ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0,
+ snd_soc_get_volsw, wm5110_put_dre),
+SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0,
+ snd_soc_get_volsw, wm5110_put_dre),
+SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0,
+ snd_soc_get_volsw, wm5110_put_dre),
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -633,29 +1027,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -904,11 +1304,11 @@ SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
- ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
- ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
@@ -1598,29 +1998,46 @@ 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)
+ goto err_adsp2_codec_probe;
+ }
- snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+ ret = snd_soc_add_codec_controls(codec,
+ arizona_adsp2_rate_controls,
+ WM5110_NUM_ADSP);
+ if (ret)
+ goto err_adsp2_codec_probe;
- priv->core.arizona->dapm = &codec->dapm;
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
return 0;
+
+err_adsp2_codec_probe:
+ for (--i; i >= 0; --i)
+ wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
+
+ return ret;
}
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
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 +2114,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/kernel/sound/soc/codecs/wm8350.c b/kernel/sound/soc/codecs/wm8350.c
index c65e5a75f..ffbf3df8a 100644
--- a/kernel/sound/soc/codecs/wm8350.c
+++ b/kernel/sound/soc/codecs/wm8350.c
@@ -394,11 +394,10 @@ static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
-static const unsigned int capture_sd_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(capture_sd_tlv,
0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
- 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0),
-};
+ 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0)
+);
static const struct snd_kcontrol_new wm8350_snd_controls[] = {
SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
@@ -1102,7 +1101,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 +1234,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/kernel/sound/soc/codecs/wm8400.c b/kernel/sound/soc/codecs/wm8400.c
index b0d84e552..b1d346aa4 100644
--- a/kernel/sound/soc/codecs/wm8400.c
+++ b/kernel/sound/soc/codecs/wm8400.c
@@ -370,10 +370,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
}
/* INMIX dB values */
-static const unsigned int in_mix_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0,7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
-};
+static const DECLARE_TLV_DB_SCALE(in_mix_tlv, -1200, 600, 0);
/* Left In PGA Connections */
static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = {
@@ -1145,7 +1142,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 +1229,6 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
diff --git a/kernel/sound/soc/codecs/wm8510.c b/kernel/sound/soc/codecs/wm8510.c
index 8736ad094..99e40e629 100644
--- a/kernel/sound/soc/codecs/wm8510.c
+++ b/kernel/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;
}
@@ -599,6 +598,7 @@ static const struct of_device_id wm8510_of_match[] = {
{ .compatible = "wlf,wm8510" },
{ },
};
+MODULE_DEVICE_TABLE(of, wm8510_of_match);
static const struct regmap_config wm8510_regmap = {
.reg_bits = 7,
@@ -644,7 +644,6 @@ static int wm8510_spi_remove(struct spi_device *spi)
static struct spi_driver wm8510_spi_driver = {
.driver = {
.name = "wm8510",
- .owner = THIS_MODULE,
.of_match_table = wm8510_of_match,
},
.probe = wm8510_spi_probe,
@@ -691,7 +690,6 @@ MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
static struct i2c_driver wm8510_i2c_driver = {
.driver = {
.name = "wm8510",
- .owner = THIS_MODULE,
.of_match_table = wm8510_of_match,
},
.probe = wm8510_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8523.c b/kernel/sound/soc/codecs/wm8523.c
index b1cc94f5f..aa287a396 100644
--- a/kernel/sound/soc/codecs/wm8523.c
+++ b/kernel/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;
}
@@ -405,6 +430,7 @@ static const struct of_device_id wm8523_of_match[] = {
{ .compatible = "wlf,wm8523" },
{ },
};
+MODULE_DEVICE_TABLE(of, wm8523_of_match);
static const struct regmap_config wm8523_regmap = {
.reg_bits = 8,
@@ -509,7 +535,6 @@ MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
static struct i2c_driver wm8523_i2c_driver = {
.driver = {
.name = "wm8523",
- .owner = THIS_MODULE,
.of_match_table = wm8523_of_match,
},
.probe = wm8523_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8580.c b/kernel/sound/soc/codecs/wm8580.c
index 0a887c5ec..66602bf02 100644
--- a/kernel/sound/soc/codecs/wm8580.c
+++ b/kernel/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;
}
@@ -917,6 +916,7 @@ static const struct of_device_id wm8580_of_match[] = {
{ .compatible = "wlf,wm8580" },
{ },
};
+MODULE_DEVICE_TABLE(of, wm8580_of_match);
static const struct regmap_config wm8580_regmap = {
.reg_bits = 7,
@@ -979,7 +979,6 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
static struct i2c_driver wm8580_i2c_driver = {
.driver = {
.name = "wm8580",
- .owner = THIS_MODULE,
.of_match_table = wm8580_of_match,
},
.probe = wm8580_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8711.c b/kernel/sound/soc/codecs/wm8711.c
index 121e46d53..c759ec068 100644
--- a/kernel/sound/soc/codecs/wm8711.c
+++ b/kernel/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;
}
@@ -432,7 +431,6 @@ static int wm8711_spi_remove(struct spi_device *spi)
static struct spi_driver wm8711_spi_driver = {
.driver = {
.name = "wm8711",
- .owner = THIS_MODULE,
.of_match_table = wm8711_of_match,
},
.probe = wm8711_spi_probe,
@@ -479,7 +477,6 @@ MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
static struct i2c_driver wm8711_i2c_driver = {
.driver = {
.name = "wm8711",
- .owner = THIS_MODULE,
.of_match_table = wm8711_of_match,
},
.probe = wm8711_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8728.c b/kernel/sound/soc/codecs/wm8728.c
index 55c7fb4fc..1564e6926 100644
--- a/kernel/sound/soc/codecs/wm8728.c
+++ b/kernel/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;
}
@@ -273,7 +272,6 @@ static int wm8728_spi_remove(struct spi_device *spi)
static struct spi_driver wm8728_spi_driver = {
.driver = {
.name = "wm8728",
- .owner = THIS_MODULE,
.of_match_table = wm8728_of_match,
},
.probe = wm8728_spi_probe,
@@ -320,7 +318,6 @@ MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
static struct i2c_driver wm8728_i2c_driver = {
.driver = {
.name = "wm8728",
- .owner = THIS_MODULE,
.of_match_table = wm8728_of_match,
},
.probe = wm8728_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8731.c b/kernel/sound/soc/codecs/wm8731.c
index 2245b6a32..4bcf5f8ec 100644
--- a/kernel/sound/soc/codecs/wm8731.c
+++ b/kernel/sound/soc/codecs/wm8731.c
@@ -79,12 +79,7 @@ static bool wm8731_volatile(struct device *dev, unsigned int reg)
return reg == WM8731_RESET;
}
-static bool wm8731_writeable(struct device *dev, unsigned int reg)
-{
- return reg <= WM8731_RESET;
-}
-
-#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0)
+#define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0)
static const char *wm8731_input_select[] = {"Line In", "Mic"};
@@ -137,7 +132,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret = 0;
if (deemph > 1)
@@ -387,6 +382,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 +417,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;
}
@@ -495,13 +491,16 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- if (wm8731->mclk)
- clk_prepare_enable(wm8731->mclk);
+ if (wm8731->mclk) {
+ ret = clk_prepare_enable(wm8731->mclk);
+ if (ret)
+ return ret;
+ }
break;
case SND_SOC_BIAS_PREPARE:
break;
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 +522,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;
}
@@ -571,69 +569,63 @@ static struct snd_soc_dai_driver wm8731_dai = {
.symmetric_rates = 1,
};
-static int wm8731_probe(struct snd_soc_codec *codec)
+static int wm8731_request_supplies(struct device *dev,
+ struct wm8731_priv *wm8731)
{
- struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
int ret = 0, i;
for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
wm8731->supplies[i].supply = wm8731_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- ret = wm8731_reset(codec);
+ return 0;
+}
+
+static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731)
+{
+ int ret = 0;
+
+ ret = wm8731_reset(wm8731->regmap);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
goto err_regulator_enable;
}
- wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* Clear POWEROFF, keep everything else disabled */
+ regmap_write(wm8731->regmap, WM8731_PWR, 0x7f);
/* Latch the update bits */
- snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
- snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0);
- snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0);
- snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0);
/* Disable bypass path by default */
- snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0);
- /* Regulators will have been enabled by bias management */
- regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
-
- return 0;
+ regcache_mark_dirty(wm8731->regmap);
err_regulator_enable:
+ /* Regulators will be enabled by bias management */
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
return ret;
}
-/* power down chip */
-static int wm8731_remove(struct snd_soc_codec *codec)
-{
- struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
-
- regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
-
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
- .probe = wm8731_probe,
- .remove = wm8731_remove,
.set_bias_level = wm8731_set_bias_level,
.suspend_bias_off = true,
@@ -658,7 +650,6 @@ static const struct regmap_config wm8731_regmap = {
.max_register = WM8731_RESET,
.volatile_reg = wm8731_volatile,
- .writeable_reg = wm8731_writeable,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = wm8731_reg_defaults,
@@ -690,6 +681,12 @@ static int wm8731_spi_probe(struct spi_device *spi)
mutex_init(&wm8731->lock);
+ spi_set_drvdata(spi, wm8731);
+
+ ret = wm8731_request_supplies(&spi->dev, wm8731);
+ if (ret != 0)
+ return ret;
+
wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
if (IS_ERR(wm8731->regmap)) {
ret = PTR_ERR(wm8731->regmap);
@@ -698,7 +695,9 @@ static int wm8731_spi_probe(struct spi_device *spi)
return ret;
}
- spi_set_drvdata(spi, wm8731);
+ ret = wm8731_hw_init(&spi->dev, wm8731);
+ if (ret != 0)
+ return ret;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8731, &wm8731_dai, 1);
@@ -719,7 +718,6 @@ static int wm8731_spi_remove(struct spi_device *spi)
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
- .owner = THIS_MODULE,
.of_match_table = wm8731_of_match,
},
.probe = wm8731_spi_probe,
@@ -754,6 +752,12 @@ static int wm8731_i2c_probe(struct i2c_client *i2c,
mutex_init(&wm8731->lock);
+ i2c_set_clientdata(i2c, wm8731);
+
+ ret = wm8731_request_supplies(&i2c->dev, wm8731);
+ if (ret != 0)
+ return ret;
+
wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
if (IS_ERR(wm8731->regmap)) {
ret = PTR_ERR(wm8731->regmap);
@@ -762,7 +766,9 @@ static int wm8731_i2c_probe(struct i2c_client *i2c,
return ret;
}
- i2c_set_clientdata(i2c, wm8731);
+ ret = wm8731_hw_init(&i2c->dev, wm8731);
+ if (ret != 0)
+ return ret;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8731, &wm8731_dai, 1);
@@ -789,7 +795,6 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
.name = "wm8731",
- .owner = THIS_MODULE,
.of_match_table = wm8731_of_match,
},
.probe = wm8731_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8737.c b/kernel/sound/soc/codecs/wm8737.c
index 51171e457..e7807601e 100644
--- a/kernel/sound/soc/codecs/wm8737.c
+++ b/kernel/sound/soc/codecs/wm8737.c
@@ -79,13 +79,12 @@ static int wm8737_reset(struct snd_soc_codec *codec)
return snd_soc_write(codec, WM8737_RESET, 0);
}
-static const unsigned int micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(micboost_tlv,
0, 0, TLV_DB_SCALE_ITEM(1300, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2800, 0, 0),
- 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(pga_tlv, -9750, 50, 1);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(ng_tlv, -7800, 600, 0);
@@ -469,7 +468,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) {
@@ -512,7 +511,6 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -562,7 +560,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);
@@ -658,7 +656,6 @@ MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id);
static struct i2c_driver wm8737_i2c_driver = {
.driver = {
.name = "wm8737",
- .owner = THIS_MODULE,
.of_match_table = wm8737_of_match,
},
.probe = wm8737_i2c_probe,
@@ -710,7 +707,6 @@ static int wm8737_spi_remove(struct spi_device *spi)
static struct spi_driver wm8737_spi_driver = {
.driver = {
.name = "wm8737",
- .owner = THIS_MODULE,
.of_match_table = wm8737_of_match,
},
.probe = wm8737_spi_probe,
diff --git a/kernel/sound/soc/codecs/wm8741.c b/kernel/sound/soc/codecs/wm8741.c
index 9e71c7689..36ef91fe0 100644
--- a/kernel/sound/soc/codecs/wm8741.c
+++ b/kernel/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;
@@ -60,25 +61,6 @@ static const struct reg_default wm8741_reg_defaults[] = {
{ 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */
};
-static bool wm8741_readable(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case WM8741_DACLLSB_ATTENUATION:
- case WM8741_DACLMSB_ATTENUATION:
- case WM8741_DACRLSB_ATTENUATION:
- case WM8741_DACRMSB_ATTENUATION:
- case WM8741_VOLUME_CONTROL:
- case WM8741_FORMAT_CONTROL:
- case WM8741_FILTER_CONTROL:
- case WM8741_MODE_CONTROL_1:
- case WM8741_MODE_CONTROL_2:
- case WM8741_ADDITIONAL_CONTROL_1:
- return true;
- default:
- return false;
- }
-}
-
static int wm8741_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, WM8741_RESET, 0);
@@ -87,13 +69,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 +106,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 +178,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 +201,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 +241,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,48 +257,40 @@ 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;
+ break;
case 11289600:
wm8741->sysclk_constraints = &constraints_11289;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 12288000:
wm8741->sysclk_constraints = &constraints_12288;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 16384000:
wm8741->sysclk_constraints = &constraints_16384;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 16934400:
wm8741->sysclk_constraints = &constraints_16934;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 18432000:
wm8741->sysclk_constraints = &constraints_18432;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 22579200:
case 33868800:
wm8741->sysclk_constraints = &constraints_22579;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 24576000:
wm8741->sysclk_constraints = &constraints_24576;
- wm8741->sysclk = freq;
- return 0;
-
+ break;
case 36864000:
wm8741->sysclk_constraints = &constraints_36864;
- wm8741->sysclk = freq;
- return 0;
+ break;
+ default:
+ return -EINVAL;
}
- return -EINVAL;
+
+ wm8741->sysclk = freq;
+ return 0;
}
static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
@@ -398,7 +372,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 +390,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 +467,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 +502,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,
@@ -489,10 +522,25 @@ static const struct regmap_config wm8741_regmap = {
.reg_defaults = wm8741_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
.cache_type = REGCACHE_RBTREE,
-
- .readable_reg = wm8741_readable,
};
+static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
+{
+ 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 +570,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,
@@ -545,7 +599,6 @@ MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
static struct i2c_driver wm8741_i2c_driver = {
.driver = {
.name = "wm8741",
- .owner = THIS_MODULE,
.of_match_table = wm8741_of_match,
},
.probe = wm8741_i2c_probe,
@@ -582,6 +635,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,
@@ -598,7 +657,6 @@ static int wm8741_spi_remove(struct spi_device *spi)
static struct spi_driver wm8741_spi_driver = {
.driver = {
.name = "wm8741",
- .owner = THIS_MODULE,
.of_match_table = wm8741_of_match,
},
.probe = wm8741_spi_probe,
diff --git a/kernel/sound/soc/codecs/wm8741.h b/kernel/sound/soc/codecs/wm8741.h
index 56c1b1d4a..c8835f65f 100644
--- a/kernel/sound/soc/codecs/wm8741.h
+++ b/kernel/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/kernel/sound/soc/codecs/wm8750.c b/kernel/sound/soc/codecs/wm8750.c
index eb0a1644b..bd9dcd216 100644
--- a/kernel/sound/soc/codecs/wm8750.c
+++ b/kernel/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;
}
@@ -778,7 +777,6 @@ MODULE_DEVICE_TABLE(spi, wm8750_spi_ids);
static struct spi_driver wm8750_spi_driver = {
.driver = {
.name = "wm8750",
- .owner = THIS_MODULE,
.of_match_table = wm8750_of_match,
},
.id_table = wm8750_spi_ids,
@@ -827,7 +825,6 @@ MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
static struct i2c_driver wm8750_i2c_driver = {
.driver = {
.name = "wm8750",
- .owner = THIS_MODULE,
.of_match_table = wm8750_of_match,
},
.probe = wm8750_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8753.c b/kernel/sound/soc/codecs/wm8753.c
index c50a59593..61299ca37 100644
--- a/kernel/sound/soc/codecs/wm8753.c
+++ b/kernel/sound/soc/codecs/wm8753.c
@@ -138,11 +138,6 @@ static bool wm8753_volatile(struct device *dev, unsigned int reg)
return reg == WM8753_RESET;
}
-static bool wm8753_writeable(struct device *dev, unsigned int reg)
-{
- return reg <= WM8753_ADCTL2;
-}
-
/* codec private data */
struct wm8753_priv {
struct regmap *regmap;
@@ -276,12 +271,11 @@ static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
-static const unsigned int out_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(out_tlv,
/* 0000000 - 0101111 = "Analogue mute" */
0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0),
- 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0),
-};
+ 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0)
+);
static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
@@ -1352,7 +1346,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 +1361,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;
}
@@ -1511,7 +1504,6 @@ static const struct regmap_config wm8753_regmap = {
.val_bits = 9,
.max_register = WM8753_ADCTL2,
- .writeable_reg = wm8753_writeable,
.volatile_reg = wm8753_volatile,
.cache_type = REGCACHE_RBTREE,
@@ -1557,7 +1549,6 @@ static int wm8753_spi_remove(struct spi_device *spi)
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
- .owner = THIS_MODULE,
.of_match_table = wm8753_of_match,
},
.probe = wm8753_spi_probe,
@@ -1610,7 +1601,6 @@ MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
static struct i2c_driver wm8753_i2c_driver = {
.driver = {
.name = "wm8753",
- .owner = THIS_MODULE,
.of_match_table = wm8753_of_match,
},
.probe = wm8753_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8770.c b/kernel/sound/soc/codecs/wm8770.c
index 53e977da2..df6178464 100644
--- a/kernel/sound/soc/codecs/wm8770.c
+++ b/kernel/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;
}
@@ -704,7 +703,6 @@ static int wm8770_spi_remove(struct spi_device *spi)
static struct spi_driver wm8770_spi_driver = {
.driver = {
.name = "wm8770",
- .owner = THIS_MODULE,
.of_match_table = wm8770_of_match,
},
.probe = wm8770_spi_probe,
diff --git a/kernel/sound/soc/codecs/wm8776.c b/kernel/sound/soc/codecs/wm8776.c
index c13050b77..5af44f9a8 100644
--- a/kernel/sound/soc/codecs/wm8776.c
+++ b/kernel/sound/soc/codecs/wm8776.c
@@ -265,7 +265,7 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
}
/* Set word length */
- switch (snd_pcm_format_width(params_format(params))) {
+ switch (params_width(params)) {
case 16:
iface = 0;
break;
@@ -280,7 +280,7 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
break;
default:
dev_err(codec->dev, "Unsupported sample size: %i\n",
- snd_pcm_format_width(params_format(params)));
+ params_width(params));
return -EINVAL;
}
@@ -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;
}
@@ -489,7 +488,6 @@ static int wm8776_spi_remove(struct spi_device *spi)
static struct spi_driver wm8776_spi_driver = {
.driver = {
.name = "wm8776",
- .owner = THIS_MODULE,
.of_match_table = wm8776_of_match,
},
.probe = wm8776_spi_probe,
@@ -537,7 +535,6 @@ MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
static struct i2c_driver wm8776_i2c_driver = {
.driver = {
.name = "wm8776",
- .owner = THIS_MODULE,
.of_match_table = wm8776_of_match,
},
.probe = wm8776_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8804-i2c.c b/kernel/sound/soc/codecs/wm8804-i2c.c
index 6596f5f3a..f27464c2c 100644
--- a/kernel/sound/soc/codecs/wm8804-i2c.c
+++ b/kernel/sound/soc/codecs/wm8804-i2c.c
@@ -49,7 +49,6 @@ MODULE_DEVICE_TABLE(of, wm8804_of_match);
static struct i2c_driver wm8804_i2c_driver = {
.driver = {
.name = "wm8804",
- .owner = THIS_MODULE,
.pm = &wm8804_pm,
.of_match_table = wm8804_of_match,
},
diff --git a/kernel/sound/soc/codecs/wm8804-spi.c b/kernel/sound/soc/codecs/wm8804-spi.c
index 407a3cf39..9998c78a2 100644
--- a/kernel/sound/soc/codecs/wm8804-spi.c
+++ b/kernel/sound/soc/codecs/wm8804-spi.c
@@ -42,7 +42,6 @@ MODULE_DEVICE_TABLE(of, wm8804_of_match);
static struct spi_driver wm8804_spi_driver = {
.driver = {
.name = "wm8804",
- .owner = THIS_MODULE,
.pm = &wm8804_pm,
.of_match_table = wm8804_of_match,
},
diff --git a/kernel/sound/soc/codecs/wm8804.c b/kernel/sound/soc/codecs/wm8804.c
index 1e403f67c..8d914702c 100644
--- a/kernel/sound/soc/codecs/wm8804.c
+++ b/kernel/sound/soc/codecs/wm8804.c
@@ -98,7 +98,7 @@ WM8804_REGULATOR_EVENT(0)
WM8804_REGULATOR_EVENT(1)
static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };
-static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);
+static SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);
static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {
SOC_DAPM_ENUM_EXT("Input Source", txsrc,
@@ -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/kernel/sound/soc/codecs/wm8900.c b/kernel/sound/soc/codecs/wm8900.c
index 2eb986c19..5d8dca88d 100644
--- a/kernel/sound/soc/codecs/wm8900.c
+++ b/kernel/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);
@@ -1267,7 +1266,6 @@ static int wm8900_spi_remove(struct spi_device *spi)
static struct spi_driver wm8900_spi_driver = {
.driver = {
.name = "wm8900",
- .owner = THIS_MODULE,
},
.probe = wm8900_spi_probe,
.remove = wm8900_spi_remove,
@@ -1313,7 +1311,6 @@ MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
static struct i2c_driver wm8900_i2c_driver = {
.driver = {
.name = "wm8900",
- .owner = THIS_MODULE,
},
.probe = wm8900_i2c_probe,
.remove = wm8900_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8903.c b/kernel/sound/soc/codecs/wm8903.c
index 04b04f8e1..e4cc41e6c 100644
--- a/kernel/sound/soc/codecs/wm8903.c
+++ b/kernel/sound/soc/codecs/wm8903.c
@@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret = 0;
if (deemph > 1)
@@ -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;
}
@@ -2195,7 +2193,6 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
static struct i2c_driver wm8903_i2c_driver = {
.driver = {
.name = "wm8903",
- .owner = THIS_MODULE,
.of_match_table = wm8903_of_match,
},
.probe = wm8903_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8904.c b/kernel/sound/soc/codecs/wm8904.c
index 215e93c1d..2aa23f1b9 100644
--- a/kernel/sound/soc/codecs/wm8904.c
+++ b/kernel/sound/soc/codecs/wm8904.c
@@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
@@ -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));
@@ -1837,7 +1837,9 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- clk_prepare_enable(wm8904->mclk);
+ ret = clk_prepare_enable(wm8904->mclk);
+ if (ret)
+ return ret;
break;
case SND_SOC_BIAS_PREPARE:
@@ -1852,7 +1854,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 +1909,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;
}
@@ -2293,7 +2294,6 @@ MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);
static struct i2c_driver wm8904_i2c_driver = {
.driver = {
.name = "wm8904",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(wm8904_of_match),
},
.probe = wm8904_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8940.c b/kernel/sound/soc/codecs/wm8940.c
index e4142b430..f6f9395ea 100644
--- a/kernel/sound/soc/codecs/wm8940.c
+++ b/kernel/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)
@@ -789,7 +787,6 @@ MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
static struct i2c_driver wm8940_i2c_driver = {
.driver = {
.name = "wm8940",
- .owner = THIS_MODULE,
},
.probe = wm8940_i2c_probe,
.remove = wm8940_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8955.c b/kernel/sound/soc/codecs/wm8955.c
index 03e04bf6c..9db00d53a 100644
--- a/kernel/sound/soc/codecs/wm8955.c
+++ b/kernel/sound/soc/codecs/wm8955.c
@@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
@@ -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);
@@ -1010,7 +1009,6 @@ MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id);
static struct i2c_driver wm8955_i2c_driver = {
.driver = {
.name = "wm8955",
- .owner = THIS_MODULE,
},
.probe = wm8955_i2c_probe,
.remove = wm8955_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8958-dsp2.c b/kernel/sound/soc/codecs/wm8958-dsp2.c
index c799cca5a..6b864c0fc 100644
--- a/kernel/sound/soc/codecs/wm8958-dsp2.c
+++ b/kernel/sound/soc/codecs/wm8958-dsp2.c
@@ -459,7 +459,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
@@ -549,7 +549,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
@@ -582,7 +582,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
@@ -749,7 +749,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
diff --git a/kernel/sound/soc/codecs/wm8960.c b/kernel/sound/soc/codecs/wm8960.c
index 8d7f63253..538079888 100644
--- a/kernel/sound/soc/codecs/wm8960.c
+++ b/kernel/sound/soc/codecs/wm8960.c
@@ -48,6 +48,9 @@
#define WM8960_DISOP 0x40
#define WM8960_DRES_MASK 0x30
+static bool is_pll_freq_available(unsigned int source, unsigned int target);
+static int wm8960_set_pll(struct snd_soc_codec *codec,
+ unsigned int freq_in, unsigned int freq_out);
/*
* wm8960 register cache
* We can't read the WM8960 register space when we are
@@ -126,7 +129,12 @@ struct wm8960_priv {
struct snd_soc_dapm_widget *rout1;
struct snd_soc_dapm_widget *out3;
bool deemph;
- int playback_fs;
+ int lrclk;
+ int bclk;
+ int sysclk;
+ int clk_id;
+ int freq_in;
+ bool is_stream_in_use[2];
struct wm8960_data pdata;
};
@@ -162,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec)
if (wm8960->deemph) {
best = 1;
for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
- if (abs(deemph_settings[i] - wm8960->playback_fs) <
- abs(deemph_settings[best] - wm8960->playback_fs))
+ if (abs(deemph_settings[i] - wm8960->lrclk) <
+ abs(deemph_settings[best] - wm8960->lrclk))
best = i;
}
@@ -193,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
@@ -203,28 +211,38 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
return wm8960_set_deemph(codec);
}
-static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
-static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
-static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1);
+static const unsigned int micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0),
+};
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
- 0, 63, 0, adc_tlv),
+ 0, 63, 0, inpga_tlv),
SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
6, 1, 0),
SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
- 7, 1, 0),
+ 7, 1, 1),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
- WM8960_INBMIX1, 4, 7, 0, boost_tlv),
+ WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
- WM8960_INBMIX1, 1, 7, 0, boost_tlv),
+ WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
- WM8960_INBMIX2, 4, 7, 0, boost_tlv),
+ WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
- WM8960_INBMIX2, 1, 7, 0, boost_tlv),
+ WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv),
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume",
+ WM8960_RINPATH, 4, 3, 0, micboost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume",
+ WM8960_LINPATH, 4, 3, 0, micboost_tlv),
SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
0, 255, 0, dac_tlv),
@@ -445,7 +463,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 +494,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 +581,124 @@ static struct {
{ 8000, 5 },
};
+/* -1 for reserved value */
+static const int sysclk_divs[] = { 1, -1, 2, -1 };
+
+/* Multiply 256 for internal 256 div */
+static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
+
+/* 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 int wm8960_configure_clocking(struct snd_soc_codec *codec)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int sysclk, bclk, lrclk, freq_out, freq_in;
+ u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
+ int i, j, k;
+
+ if (!(iface1 & (1<<6))) {
+ dev_dbg(codec->dev,
+ "Codec is slave mode, no need to configure clock\n");
+ return 0;
+ }
+
+ if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) {
+ dev_err(codec->dev, "No MCLK configured\n");
+ return -EINVAL;
+ }
+
+ freq_in = wm8960->freq_in;
+ bclk = wm8960->bclk;
+ lrclk = wm8960->lrclk;
+ /*
+ * If it's sysclk auto mode, check if the MCLK can provide sysclk or
+ * not. If MCLK can provide sysclk, using MCLK to provide sysclk
+ * directly. Otherwise, auto select a available pll out frequency
+ * and set PLL.
+ */
+ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) {
+ /* disable the PLL and using MCLK to provide sysclk */
+ wm8960_set_pll(codec, 0, 0);
+ freq_out = freq_in;
+ } else if (wm8960->sysclk) {
+ freq_out = wm8960->sysclk;
+ } else {
+ dev_err(codec->dev, "No SYSCLK configured\n");
+ return -EINVAL;
+ }
+
+ /* check if the sysclk frequency is available. */
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ sysclk = freq_out / sysclk_divs[i];
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ if (sysclk == dac_divs[j] * lrclk) {
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
+ if (sysclk == bclk * bclk_divs[k] / 10)
+ break;
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ }
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
+ }
+
+ if (i != ARRAY_SIZE(sysclk_divs)) {
+ goto configure_clock;
+ } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
+ dev_err(codec->dev, "failed to configure clock\n");
+ return -EINVAL;
+ }
+ /* get a available pll out frequency and set pll */
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ sysclk = lrclk * dac_divs[j];
+ freq_out = sysclk * sysclk_divs[i];
+
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ if (sysclk == bclk * bclk_divs[k] / 10 &&
+ is_pll_freq_available(freq_in, freq_out)) {
+ wm8960_set_pll(codec,
+ freq_in, freq_out);
+ break;
+ } else {
+ continue;
+ }
+ }
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(sysclk_divs)) {
+ dev_err(codec->dev, "failed to configure clock\n");
+ return -EINVAL;
+ }
+
+configure_clock:
+ /* configure sysclk clock */
+ snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1);
+
+ /* configure frame clock */
+ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3);
+ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6);
+
+ /* configure bit clock */
+ snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k);
+
+ return 0;
+}
+
static int wm8960_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -570,8 +706,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,15 +723,21 @@ 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));
return -EINVAL;
}
+ wm8960->lrclk = params_rate(params);
/* Update filters for the new rate */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- wm8960->playback_fs = params_rate(params);
+ if (tx) {
wm8960_set_deemph(codec);
} else {
for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
@@ -602,6 +749,25 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
/* set iface */
snd_soc_write(codec, WM8960_IFACE1, iface);
+
+ wm8960->is_stream_in_use[tx] = true;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON &&
+ !wm8960->is_stream_in_use[!tx])
+ return wm8960_configure_clocking(codec);
+
+ return 0;
+}
+
+static int wm8960_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ wm8960->is_stream_in_use[tx] = false;
+
return 0;
}
@@ -620,6 +786,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
int ret;
switch (level) {
@@ -627,7 +794,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);
@@ -639,11 +806,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
}
}
+ ret = wm8960_configure_clocking(codec);
+ if (ret)
+ return ret;
+
/* Set VMID to 2x50k */
snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
break;
case SND_SOC_BIAS_ON:
+ /*
+ * If it's sysclk auto mode, and the pll is enabled,
+ * disable the pll
+ */
+ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
+ wm8960_set_pll(codec, 0, 0);
+
if (!IS_ERR(wm8960->mclk))
clk_disable_unprepare(wm8960->mclk);
break;
@@ -655,7 +833,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 +869,6 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -700,6 +876,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
int reg, ret;
switch (level) {
@@ -707,7 +884,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,
@@ -751,9 +928,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
return ret;
}
}
+
+ ret = wm8960_configure_clocking(codec);
+ if (ret)
+ return ret;
+
break;
case SND_SOC_BIAS_ON:
+ /*
+ * If it's sysclk auto mode, and the pll is enabled,
+ * disable the pll
+ */
+ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
+ wm8960_set_pll(codec, 0, 0);
+
if (!IS_ERR(wm8960->mclk))
clk_disable_unprepare(wm8960->mclk);
@@ -778,7 +967,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 +991,6 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -814,6 +1001,28 @@ struct _pll_div {
u32 k:24;
};
+static bool is_pll_freq_available(unsigned int source, unsigned int target)
+{
+ unsigned int Ndiv;
+
+ if (source == 0 || target == 0)
+ return false;
+
+ /* Scale up target to PLL operating frequency */
+ target *= 4;
+ Ndiv = target / source;
+
+ if (Ndiv < 6) {
+ source >>= 1;
+ Ndiv = target / source;
+ }
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ return false;
+
+ return true;
+}
+
/* The size in bits of the pll divide multiplied by 10
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 24) * 10)
@@ -865,10 +1074,9 @@ static int pll_factors(unsigned int source, unsigned int target,
return 0;
}
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- int source, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_pll(struct snd_soc_codec *codec,
+ unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
static struct _pll_div pll_div;
int ret;
@@ -908,6 +1116,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return 0;
}
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+ wm8960->freq_in = freq_in;
+
+ if (pll_id == WM8960_SYSCLK_AUTO)
+ return 0;
+
+ return wm8960_set_pll(codec, freq_in, freq_out);
+}
+
static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
@@ -950,18 +1172,47 @@ 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;
+ case WM8960_SYSCLK_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8960->sysclk = freq;
+ wm8960->clk_id = clk_id;
+
+ 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,
+ .hw_free = wm8960_hw_free,
.digital_mute = wm8960_mute,
.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 = {
@@ -1113,7 +1364,6 @@ MODULE_DEVICE_TABLE(of, wm8960_of_match);
static struct i2c_driver wm8960_i2c_driver = {
.driver = {
.name = "wm8960",
- .owner = THIS_MODULE,
.of_match_table = wm8960_of_match,
},
.probe = wm8960_i2c_probe,
diff --git a/kernel/sound/soc/codecs/wm8960.h b/kernel/sound/soc/codecs/wm8960.h
index 2d8163d70..ab3220d34 100644
--- a/kernel/sound/soc/codecs/wm8960.h
+++ b/kernel/sound/soc/codecs/wm8960.h
@@ -82,6 +82,7 @@
#define WM8960_SYSCLK_MCLK (0 << 0)
#define WM8960_SYSCLK_PLL (1 << 0)
+#define WM8960_SYSCLK_AUTO (2 << 0)
#define WM8960_DAC_DIV_1 (0 << 3)
#define WM8960_DAC_DIV_1_5 (1 << 3)
diff --git a/kernel/sound/soc/codecs/wm8961.c b/kernel/sound/soc/codecs/wm8961.c
index 95e2c1bfc..e30446a04 100644
--- a/kernel/sound/soc/codecs/wm8961.c
+++ b/kernel/sound/soc/codecs/wm8961.c
@@ -331,13 +331,12 @@ static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
-static unsigned int boost_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(boost_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(13, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(20, 0, 0),
- 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0);
static const struct snd_kcontrol_new wm8961_snd_controls[] = {
@@ -758,7 +757,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 +772,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 +794,6 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -984,7 +981,6 @@ MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
static struct i2c_driver wm8961_i2c_driver = {
.driver = {
.name = "wm8961",
- .owner = THIS_MODULE,
},
.probe = wm8961_i2c_probe,
.remove = wm8961_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8962.c b/kernel/sound/soc/codecs/wm8962.c
index 118b0034b..a7e79784f 100644
--- a/kernel/sound/soc/codecs/wm8962.c
+++ b/kernel/sound/soc/codecs/wm8962.c
@@ -113,7 +113,7 @@ WM8962_REGULATOR_EVENT(5)
WM8962_REGULATOR_EVENT(6)
WM8962_REGULATOR_EVENT(7)
-static struct reg_default wm8962_reg[] = {
+static const struct reg_default wm8962_reg[] = {
{ 0, 0x009F }, /* R0 - Left Input volume */
{ 1, 0x049F }, /* R1 - Right Input volume */
{ 2, 0x0000 }, /* R2 - HPOUTL volume */
@@ -365,8 +365,8 @@ static struct reg_default wm8962_reg[] = {
{ 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */
{ 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */
- { 17048, 0x0083 }, /* R17408 - HPF_C_1 */
- { 17049, 0x98AD }, /* R17409 - HPF_C_0 */
+ { 17408, 0x0083 }, /* R17408 - HPF_C_1 */
+ { 17409, 0x98AD }, /* R17409 - HPF_C_0 */
{ 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */
{ 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */
@@ -1456,14 +1456,13 @@ static int wm8962_reset(struct wm8962_priv *wm8962)
static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0);
static const DECLARE_TLV_DB_SCALE(mixin_tlv, -1500, 300, 0);
-static const unsigned int mixinpga_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(mixinpga_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 2, TLV_DB_SCALE_ITEM(1300, 1300, 0),
3, 4, TLV_DB_SCALE_ITEM(1800, 200, 0),
5, 5, TLV_DB_SCALE_ITEM(2400, 0, 0),
- 6, 7, TLV_DB_SCALE_ITEM(2700, 300, 0),
-};
+ 6, 7, TLV_DB_SCALE_ITEM(2700, 300, 0)
+);
static const DECLARE_TLV_DB_SCALE(beep_tlv, -9600, 600, 1);
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0);
@@ -1471,11 +1470,10 @@ static const DECLARE_TLV_DB_SCALE(inmix_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static const DECLARE_TLV_DB_SCALE(hp_tlv, -700, 100, 0);
-static const unsigned int classd_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(classd_tlv,
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
- 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
-};
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
@@ -2361,7 +2359,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 +2444,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 +2508,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 +2525,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 +2533,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -2614,7 +2608,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;
@@ -2950,7 +2944,8 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute)
WM8962_DAC_MUTE, val);
}
-#define WM8962_RATES SNDRV_PCM_RATE_8000_96000
+#define WM8962_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -3118,7 +3113,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 +3159,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 +3410,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 +3458,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");
@@ -3498,7 +3494,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
};
/* Improve power consumption for IN4 DC measurement mode */
-static const struct reg_default wm8962_dc_measure[] = {
+static const struct reg_sequence wm8962_dc_measure[] = {
{ 0xfd, 0x1 },
{ 0xcc, 0x40 },
{ 0xfd, 0 },
@@ -3764,7 +3760,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8962, &wm8962_dai, 1);
if (ret < 0)
- goto err_enable;
+ goto err_pm_runtime;
regcache_cache_only(wm8962->regmap, true);
@@ -3773,6 +3769,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
return 0;
+err_pm_runtime:
+ pm_runtime_disable(&i2c->dev);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies);
err:
@@ -3782,6 +3780,7 @@ err:
static int wm8962_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
+ pm_runtime_disable(&client->dev);
return 0;
}
@@ -3809,6 +3808,8 @@ static int wm8962_runtime_resume(struct device *dev)
wm8962_reset(wm8962);
+ regcache_mark_dirty(wm8962->regmap);
+
/* SYSCLK defaults to on; make sure it is off so we can safely
* write to registers if the device is declocked.
*/
@@ -3862,7 +3863,7 @@ static int wm8962_runtime_suspend(struct device *dev)
}
#endif
-static struct dev_pm_ops wm8962_pm = {
+static const struct dev_pm_ops wm8962_pm = {
SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL)
};
@@ -3881,7 +3882,6 @@ MODULE_DEVICE_TABLE(of, wm8962_of_match);
static struct i2c_driver wm8962_i2c_driver = {
.driver = {
.name = "wm8962",
- .owner = THIS_MODULE,
.of_match_table = wm8962_of_match,
.pm = &wm8962_pm,
},
diff --git a/kernel/sound/soc/codecs/wm8971.c b/kernel/sound/soc/codecs/wm8971.c
index f9cbabdc6..2cdde32c4 100644
--- a/kernel/sound/soc/codecs/wm8971.c
+++ b/kernel/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;
}
@@ -711,7 +710,6 @@ MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
static struct i2c_driver wm8971_i2c_driver = {
.driver = {
.name = "wm8971",
- .owner = THIS_MODULE,
},
.probe = wm8971_i2c_probe,
.remove = wm8971_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8974.c b/kernel/sound/soc/codecs/wm8974.c
index ff0e4646b..4c29bd2ae 100644
--- a/kernel/sound/soc/codecs/wm8974.c
+++ b/kernel/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;
}
@@ -575,6 +574,7 @@ static const struct regmap_config wm8974_regmap = {
.max_register = WM8974_MONOMIX,
.reg_defaults = wm8974_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
};
static int wm8974_probe(struct snd_soc_codec *codec)
@@ -635,7 +635,6 @@ MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
static struct i2c_driver wm8974_i2c_driver = {
.driver = {
.name = "wm8974",
- .owner = THIS_MODULE,
},
.probe = wm8974_i2c_probe,
.remove = wm8974_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8978.c b/kernel/sound/soc/codecs/wm8978.c
index cf7032911..d36d6001f 100644
--- a/kernel/sound/soc/codecs/wm8978.c
+++ b/kernel/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 */
@@ -1073,7 +1072,6 @@ MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
static struct i2c_driver wm8978_i2c_driver = {
.driver = {
.name = "wm8978",
- .owner = THIS_MODULE,
},
.probe = wm8978_i2c_probe,
.remove = wm8978_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8983.c b/kernel/sound/soc/codecs/wm8983.c
index 5d1cf08a7..7350ff654 100644
--- a/kernel/sound/soc/codecs/wm8983.c
+++ b/kernel/sound/soc/codecs/wm8983.c
@@ -84,66 +84,6 @@ static const struct reg_default wm8983_defaults[] = {
{ 0x3D, 0x0000 }, /* R61 - BIAS CTRL */
};
-static const struct wm8983_reg_access {
- u16 read; /* Mask of readable bits */
- u16 write; /* Mask of writable bits */
-} wm8983_access_masks[WM8983_MAX_REGISTER + 1] = {
- [0x00] = { 0x0000, 0x01FF }, /* R0 - Software Reset */
- [0x01] = { 0x0000, 0x01FF }, /* R1 - Power management 1 */
- [0x02] = { 0x0000, 0x01FF }, /* R2 - Power management 2 */
- [0x03] = { 0x0000, 0x01EF }, /* R3 - Power management 3 */
- [0x04] = { 0x0000, 0x01FF }, /* R4 - Audio Interface */
- [0x05] = { 0x0000, 0x003F }, /* R5 - Companding control */
- [0x06] = { 0x0000, 0x01FD }, /* R6 - Clock Gen control */
- [0x07] = { 0x0000, 0x000F }, /* R7 - Additional control */
- [0x08] = { 0x0000, 0x003F }, /* R8 - GPIO Control */
- [0x09] = { 0x0000, 0x0070 }, /* R9 - Jack Detect Control 1 */
- [0x0A] = { 0x0000, 0x004F }, /* R10 - DAC Control */
- [0x0B] = { 0x0000, 0x01FF }, /* R11 - Left DAC digital Vol */
- [0x0C] = { 0x0000, 0x01FF }, /* R12 - Right DAC digital vol */
- [0x0D] = { 0x0000, 0x00FF }, /* R13 - Jack Detect Control 2 */
- [0x0E] = { 0x0000, 0x01FB }, /* R14 - ADC Control */
- [0x0F] = { 0x0000, 0x01FF }, /* R15 - Left ADC Digital Vol */
- [0x10] = { 0x0000, 0x01FF }, /* R16 - Right ADC Digital Vol */
- [0x12] = { 0x0000, 0x017F }, /* R18 - EQ1 - low shelf */
- [0x13] = { 0x0000, 0x017F }, /* R19 - EQ2 - peak 1 */
- [0x14] = { 0x0000, 0x017F }, /* R20 - EQ3 - peak 2 */
- [0x15] = { 0x0000, 0x017F }, /* R21 - EQ4 - peak 3 */
- [0x16] = { 0x0000, 0x007F }, /* R22 - EQ5 - high shelf */
- [0x18] = { 0x0000, 0x01FF }, /* R24 - DAC Limiter 1 */
- [0x19] = { 0x0000, 0x007F }, /* R25 - DAC Limiter 2 */
- [0x1B] = { 0x0000, 0x01FF }, /* R27 - Notch Filter 1 */
- [0x1C] = { 0x0000, 0x017F }, /* R28 - Notch Filter 2 */
- [0x1D] = { 0x0000, 0x017F }, /* R29 - Notch Filter 3 */
- [0x1E] = { 0x0000, 0x017F }, /* R30 - Notch Filter 4 */
- [0x20] = { 0x0000, 0x01BF }, /* R32 - ALC control 1 */
- [0x21] = { 0x0000, 0x00FF }, /* R33 - ALC control 2 */
- [0x22] = { 0x0000, 0x01FF }, /* R34 - ALC control 3 */
- [0x23] = { 0x0000, 0x000F }, /* R35 - Noise Gate */
- [0x24] = { 0x0000, 0x001F }, /* R36 - PLL N */
- [0x25] = { 0x0000, 0x003F }, /* R37 - PLL K 1 */
- [0x26] = { 0x0000, 0x01FF }, /* R38 - PLL K 2 */
- [0x27] = { 0x0000, 0x01FF }, /* R39 - PLL K 3 */
- [0x29] = { 0x0000, 0x000F }, /* R41 - 3D control */
- [0x2A] = { 0x0000, 0x01E7 }, /* R42 - OUT4 to ADC */
- [0x2B] = { 0x0000, 0x01BF }, /* R43 - Beep control */
- [0x2C] = { 0x0000, 0x0177 }, /* R44 - Input ctrl */
- [0x2D] = { 0x0000, 0x01FF }, /* R45 - Left INP PGA gain ctrl */
- [0x2E] = { 0x0000, 0x01FF }, /* R46 - Right INP PGA gain ctrl */
- [0x2F] = { 0x0000, 0x0177 }, /* R47 - Left ADC BOOST ctrl */
- [0x30] = { 0x0000, 0x0177 }, /* R48 - Right ADC BOOST ctrl */
- [0x31] = { 0x0000, 0x007F }, /* R49 - Output ctrl */
- [0x32] = { 0x0000, 0x01FF }, /* R50 - Left mixer ctrl */
- [0x33] = { 0x0000, 0x01FF }, /* R51 - Right mixer ctrl */
- [0x34] = { 0x0000, 0x01FF }, /* R52 - LOUT1 (HP) volume ctrl */
- [0x35] = { 0x0000, 0x01FF }, /* R53 - ROUT1 (HP) volume ctrl */
- [0x36] = { 0x0000, 0x01FF }, /* R54 - LOUT2 (SPK) volume ctrl */
- [0x37] = { 0x0000, 0x01FF }, /* R55 - ROUT2 (SPK) volume ctrl */
- [0x38] = { 0x0000, 0x004F }, /* R56 - OUT3 mixer ctrl */
- [0x39] = { 0x0000, 0x00FF }, /* R57 - OUT4 (MONO) mix ctrl */
- [0x3D] = { 0x0000, 0x0100 } /* R61 - BIAS CTRL */
-};
-
/* vol/gain update regs */
static const int vol_update_regs[] = {
WM8983_LEFT_DAC_DIGITAL_VOL,
@@ -605,12 +545,19 @@ static int eqmode_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static bool wm8983_readable(struct device *dev, unsigned int reg)
+static bool wm8983_writeable(struct device *dev, unsigned int reg)
{
- if (reg > WM8983_MAX_REGISTER)
- return 0;
-
- return wm8983_access_masks[reg].read != 0;
+ switch (reg) {
+ case WM8983_SOFTWARE_RESET ... WM8983_RIGHT_ADC_DIGITAL_VOL:
+ case WM8983_EQ1_LOW_SHELF ... WM8983_DAC_LIMITER_2:
+ case WM8983_NOTCH_FILTER_1 ... WM8983_NOTCH_FILTER_4:
+ case WM8983_ALC_CONTROL_1 ... WM8983_PLL_K_3:
+ case WM8983_3D_CONTROL ... WM8983_OUT4_MONO_MIX_CTRL:
+ case WM8983_BIAS_CTRL:
+ return true;
+ default:
+ return false;
+ }
}
static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute)
@@ -915,7 +862,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 +910,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1049,8 +995,9 @@ static const struct regmap_config wm8983_regmap = {
.reg_defaults = wm8983_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8983_defaults),
.cache_type = REGCACHE_RBTREE,
+ .max_register = WM8983_MAX_REGISTER,
- .readable_reg = wm8983_readable,
+ .writeable_reg = wm8983_writeable,
};
#if defined(CONFIG_SPI_MASTER)
@@ -1086,7 +1033,6 @@ static int wm8983_spi_remove(struct spi_device *spi)
static struct spi_driver wm8983_spi_driver = {
.driver = {
.name = "wm8983",
- .owner = THIS_MODULE,
},
.probe = wm8983_spi_probe,
.remove = wm8983_spi_remove
@@ -1134,7 +1080,6 @@ MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id);
static struct i2c_driver wm8983_i2c_driver = {
.driver = {
.name = "wm8983",
- .owner = THIS_MODULE,
},
.probe = wm8983_i2c_probe,
.remove = wm8983_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8985.c b/kernel/sound/soc/codecs/wm8985.c
index 0b3b54c99..9918152a0 100644
--- a/kernel/sound/soc/codecs/wm8985.c
+++ b/kernel/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;
}
@@ -1097,7 +1096,6 @@ static int wm8985_spi_remove(struct spi_device *spi)
static struct spi_driver wm8985_spi_driver = {
.driver = {
.name = "wm8985",
- .owner = THIS_MODULE,
},
.probe = wm8985_spi_probe,
.remove = wm8985_spi_remove
@@ -1145,7 +1143,6 @@ MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
static struct i2c_driver wm8985_i2c_driver = {
.driver = {
.name = "wm8985",
- .owner = THIS_MODULE,
},
.probe = wm8985_i2c_probe,
.remove = wm8985_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8988.c b/kernel/sound/soc/codecs/wm8988.c
index 24968aa86..895721a25 100644
--- a/kernel/sound/soc/codecs/wm8988.c
+++ b/kernel/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;
}
@@ -872,7 +871,6 @@ static int wm8988_spi_remove(struct spi_device *spi)
static struct spi_driver wm8988_spi_driver = {
.driver = {
.name = "wm8988",
- .owner = THIS_MODULE,
},
.probe = wm8988_spi_probe,
.remove = wm8988_spi_remove,
@@ -920,7 +918,6 @@ MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
static struct i2c_driver wm8988_i2c_driver = {
.driver = {
.name = "wm8988",
- .owner = THIS_MODULE,
},
.probe = wm8988_i2c_probe,
.remove = wm8988_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8990.c b/kernel/sound/soc/codecs/wm8990.c
index c93bffcb3..23ecd30d8 100644
--- a/kernel/sound/soc/codecs/wm8990.c
+++ b/kernel/sound/soc/codecs/wm8990.c
@@ -418,10 +418,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
/* INMIX dB values */
-static const unsigned int in_mix_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
-};
+static const DECLARE_TLV_DB_SCALE(in_mix_tlv, -1200, 600, 0);
/* Left In PGA Connections */
static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = {
@@ -1124,7 +1121,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 +1224,6 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1281,7 +1277,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);
@@ -1357,7 +1353,6 @@ MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);
static struct i2c_driver wm8990_i2c_driver = {
.driver = {
.name = "wm8990",
- .owner = THIS_MODULE,
},
.probe = wm8990_i2c_probe,
.remove = wm8990_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8991.c b/kernel/sound/soc/codecs/wm8991.c
index 49df0dc60..c9ee0ac6a 100644
--- a/kernel/sound/soc/codecs/wm8991.c
+++ b/kernel/sound/soc/codecs/wm8991.c
@@ -111,45 +111,14 @@ static bool wm8991_volatile(struct device *dev, unsigned int reg)
}
}
-static const unsigned int rec_mix_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 7, TLV_DB_LINEAR_ITEM(-1500, 600),
-};
-
-static const unsigned int in_pga_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000),
-};
-
-static const unsigned int out_mix_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 7, TLV_DB_LINEAR_ITEM(0, -2100),
-};
-
-static const unsigned int out_pga_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 127, TLV_DB_LINEAR_ITEM(-7300, 600),
-};
-
-static const unsigned int out_omix_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 7, TLV_DB_LINEAR_ITEM(-600, 0),
-};
-
-static const unsigned int out_dac_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 255, TLV_DB_LINEAR_ITEM(-7163, 0),
-};
-
-static const unsigned int in_adc_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763),
-};
-
-static const unsigned int out_sidetone_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 31, TLV_DB_LINEAR_ITEM(-3600, 0),
-};
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -429,10 +398,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
/* INMIX dB values */
-static const unsigned int in_mix_tlv[] = {
- TLV_DB_RANGE_HEAD(1),
- 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
-};
+static const DECLARE_TLV_DB_LINEAR(in_mix_tlv, -1200, 600);
/* Left In PGA Connections */
static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
@@ -1131,7 +1097,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 +1190,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1364,7 +1329,6 @@ MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
static struct i2c_driver wm8991_i2c_driver = {
.driver = {
.name = "wm8991",
- .owner = THIS_MODULE,
},
.probe = wm8991_i2c_probe,
.remove = wm8991_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8993.c b/kernel/sound/soc/codecs/wm8993.c
index 2e70a270e..8668c4c39 100644
--- a/kernel/sound/soc/codecs/wm8993.c
+++ b/kernel/sound/soc/codecs/wm8993.c
@@ -41,7 +41,7 @@ static const char *wm8993_supply_names[WM8993_NUM_SUPPLIES] = {
"SPKVDD",
};
-static struct reg_default wm8993_reg_defaults[] = {
+static const struct reg_default wm8993_reg_defaults[] = {
{ 1, 0x0000 }, /* R1 - Power Management (1) */
{ 2, 0x6000 }, /* R2 - Power Management (2) */
{ 3, 0x0000 }, /* R3 - Power Management (3) */
@@ -628,11 +628,10 @@ static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0);
static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0);
static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
-static const unsigned int drc_max_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(drc_max_tlv,
0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
- 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
@@ -992,7 +991,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 +1064,6 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1485,7 +1482,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 +1536,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 +1560,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 +1570,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) {
@@ -1597,7 +1594,7 @@ static int wm8993_resume(struct snd_soc_codec *codec)
#endif
/* Tune DC servo configuration */
-static struct reg_default wm8993_regmap_patch[] = {
+static const struct reg_sequence wm8993_regmap_patch[] = {
{ 0x44, 3 },
{ 0x56, 3 },
{ 0x44, 0 },
@@ -1744,7 +1741,6 @@ MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
static struct i2c_driver wm8993_i2c_driver = {
.driver = {
.name = "wm8993",
- .owner = THIS_MODULE,
},
.probe = wm8993_i2c_probe,
.remove = wm8993_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8994.c b/kernel/sound/soc/codecs/wm8994.c
index a1c04dab6..a18aecb49 100644
--- a/kernel/sound/soc/codecs/wm8994.c
+++ b/kernel/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);
@@ -361,7 +362,7 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int drc = wm8994_get_drc(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (drc < 0)
return drc;
@@ -468,7 +469,7 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (block < 0)
return block;
@@ -1941,14 +1942,16 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF2ADCDAT", NULL, "AIF2ADC Mux" },
/* AIF3 output */
- { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1L" },
- { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1R" },
- { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2L" },
- { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2R" },
- { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCL" },
- { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCR" },
- { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
- { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
+ { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC1L" },
+ { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC1R" },
+ { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC2L" },
+ { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC2R" },
+ { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
+ { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
+ { "AIF3ADC Mux", "AIF2DACDAT", "AIF2DACL" },
+ { "AIF3ADC Mux", "AIF2DACDAT", "AIF2DACR" },
+
+ { "AIF3ADCDAT", NULL, "AIF3ADC Mux" },
/* Loopback */
{ "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" },
@@ -2492,12 +2495,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 +2524,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 +2544,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 +3164,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 +3357,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 +3372,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 +3413,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 +3503,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 +3514,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 +3623,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 +3662,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 +3723,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 +3769,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 +3783,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 +3819,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 +3846,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 +3985,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 +4018,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 +4026,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 +4086,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 +4135,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/kernel/sound/soc/codecs/wm8995.c b/kernel/sound/soc/codecs/wm8995.c
index 66103c2b0..24500bafb 100644
--- a/kernel/sound/soc/codecs/wm8995.c
+++ b/kernel/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;
}
@@ -2246,7 +2246,6 @@ static int wm8995_spi_remove(struct spi_device *spi)
static struct spi_driver wm8995_spi_driver = {
.driver = {
.name = "wm8995",
- .owner = THIS_MODULE,
},
.probe = wm8995_spi_probe,
.remove = wm8995_spi_remove
@@ -2298,7 +2297,6 @@ MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id);
static struct i2c_driver wm8995_i2c_driver = {
.driver = {
.name = "wm8995",
- .owner = THIS_MODULE,
},
.probe = wm8995_i2c_probe,
.remove = wm8995_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8996.c b/kernel/sound/soc/codecs/wm8996.c
index 308748a02..f7ccd9fc5 100644
--- a/kernel/sound/soc/codecs/wm8996.c
+++ b/kernel/sound/soc/codecs/wm8996.c
@@ -117,7 +117,7 @@ WM8996_REGULATOR_EVENT(0)
WM8996_REGULATOR_EVENT(1)
WM8996_REGULATOR_EVENT(2)
-static struct reg_default wm8996_reg[] = {
+static const struct reg_default wm8996_reg[] = {
{ WM8996_POWER_MANAGEMENT_1, 0x0 },
{ WM8996_POWER_MANAGEMENT_2, 0x0 },
{ WM8996_POWER_MANAGEMENT_3, 0x0 },
@@ -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;
}
@@ -1782,7 +1780,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream,
wm8996->rx_rate[dai->id] = params_rate(params);
/* Needs looking at for TDM */
- bits = snd_pcm_format_width(params_format(params));
+ bits = params_width(params);
if (bits < 0)
return bits;
aifdata |= (bits << WM8996_AIF1TX_WL_SHIFT) | bits;
@@ -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);
@@ -3097,7 +3098,6 @@ MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id);
static struct i2c_driver wm8996_i2c_driver = {
.driver = {
.name = "wm8996",
- .owner = THIS_MODULE,
},
.probe = wm8996_i2c_probe,
.remove = wm8996_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm8997.c b/kernel/sound/soc/codecs/wm8997.c
index e7c81baef..b4dba3a02 100644
--- a/kernel/sound/soc/codecs/wm8997.c
+++ b/kernel/sound/soc/codecs/wm8997.c
@@ -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[] = {
@@ -172,8 +174,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
-SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
-SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2),
SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
@@ -185,8 +186,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
-SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2),
SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
@@ -198,8 +198,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
-SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2),
SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
@@ -211,8 +210,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
24, 0, eq_tlv),
-SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
-SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2),
SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
24, 0, eq_tlv),
SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
@@ -240,10 +238,10 @@ SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
-SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
-SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
-SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
-SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2),
+ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
+ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
+ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
@@ -409,7 +407,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 +1054,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 +1126,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/kernel/sound/soc/codecs/wm8998.c b/kernel/sound/soc/codecs/wm8998.c
new file mode 100644
index 000000000..8782dfb62
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8998.c
@@ -0,0 +1,1430 @@
+/*
+ * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm8998.h"
+
+struct wm8998_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static int wm8998_asrc_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);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = snd_soc_read(codec, ARIZONA_ASRC_RATE1);
+ val &= ARIZONA_ASRC_RATE1_MASK;
+ val >>= ARIZONA_ASRC_RATE1_SHIFT;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ val = snd_soc_read(codec,
+ ARIZONA_SAMPLE_RATE_1 + val);
+ if (val >= 0x11) {
+ dev_warn(codec->dev,
+ "Unsupported ASRC rate1 (%s)\n",
+ arizona_sample_rate_val_to_name(val));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev,
+ "Illegal ASRC rate1 selector (0x%x)\n",
+ val);
+ return -EINVAL;
+ }
+
+ val = snd_soc_read(codec, ARIZONA_ASRC_RATE2);
+ val &= ARIZONA_ASRC_RATE2_MASK;
+ val >>= ARIZONA_ASRC_RATE2_SHIFT;
+
+ switch (val) {
+ case 8:
+ case 9:
+ val -= 0x8;
+ val = snd_soc_read(codec,
+ ARIZONA_ASYNC_SAMPLE_RATE_1 + val);
+ if (val >= 0x11) {
+ dev_warn(codec->dev,
+ "Unsupported ASRC rate2 (%s)\n",
+ arizona_sample_rate_val_to_name(val));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev,
+ "Illegal ASRC rate2 selector (0x%x)\n",
+ val);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8998_in1mux_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 wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = wm8998->core.arizona;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, inmode;
+ unsigned int mode_val, src_val;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ /* L and R registers have same shift and mask */
+ inmode = arizona->pdata.inmode[2 * mux];
+ src_val = mux << ARIZONA_IN1L_SRC_SHIFT;
+ if (inmode & ARIZONA_INMODE_SE)
+ src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT;
+
+ switch (arizona->pdata.inmode[0]) {
+ case ARIZONA_INMODE_DMIC:
+ if (mux)
+ mode_val = 0; /* B always analogue */
+ else
+ mode_val = 1 << ARIZONA_IN1_MODE_SHIFT;
+
+ snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_MODE_MASK, mode_val);
+
+ /* IN1A is digital so L and R must change together */
+ /* src_val setting same for both registers */
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK, src_val);
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_SRC_MASK |
+ ARIZONA_IN1R_SRC_SE_MASK, src_val);
+ break;
+ default:
+ /* both analogue */
+ snd_soc_update_bits(codec,
+ e->reg,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK,
+ src_val);
+ break;
+ }
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static int wm8998_in2mux_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 wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = wm8998->core.arizona;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, inmode, src_val, mode_val;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ inmode = arizona->pdata.inmode[1 + (2 * mux)];
+ if (inmode & ARIZONA_INMODE_DMIC)
+ mode_val = 1 << ARIZONA_IN2_MODE_SHIFT;
+ else
+ mode_val = 0;
+
+ src_val = mux << ARIZONA_IN2L_SRC_SHIFT;
+ if (inmode & ARIZONA_INMODE_SE)
+ src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT;
+
+ snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_MODE_MASK, mode_val);
+
+ snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK,
+ src_val);
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static const char * const wm8998_inmux_texts[] = {
+ "A",
+ "B",
+};
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const struct snd_kcontrol_new wm8998_in1mux[2] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+};
+
+static const struct snd_kcontrol_new wm8998_in2mux =
+ SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in2mux_put);
+
+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(ng_tlv, -10200, 600, 0);
+
+#define WM8998_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0)
+
+static const struct snd_kcontrol_new wm8998_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+
+ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L),
+WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R),
+WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+static const char * const wm8998_aec_loopback_texts[] = {
+ "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT",
+ "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR",
+};
+
+static const unsigned int wm8998_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 6, 7, 8, 9,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
+ ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ wm8998_aec_loopback_texts,
+ wm8998_aec_loopback_values);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
+ ARIZONA_DAC_AEC_CONTROL_2,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ wm8998_aec_loopback_texts,
+ wm8998_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+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,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1AL"),
+SND_SOC_DAPM_INPUT("IN1AR"),
+SND_SOC_DAPM_INPUT("IN1BL"),
+SND_SOC_DAPM_INPUT("IN1BR"),
+SND_SOC_DAPM_INPUT("IN2A"),
+SND_SOC_DAPM_INPUT("IN2B"),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]),
+SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[0]),
+
+SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+ARIZONA_MUX_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MUX_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MUX_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MUX_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"),
+ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"),
+
+ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"),
+ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDATL"),
+SND_SOC_DAPM_OUTPUT("SPKDATR"),
+SND_SOC_DAPM_OUTPUT("SPDIF"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2 PGA" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1L", "ASRC1L" }, \
+ { name, "ASRC1R", "ASRC1R" }, \
+ { name, "ASRC2L", "ASRC2L" }, \
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT2L", NULL, "CPVDD" },
+ { "OUT2R", NULL, "CPVDD" },
+ { "OUT3", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "IN1AL", NULL, "SYSCLK" },
+ { "IN1AR", NULL, "SYSCLK" },
+ { "IN1BL", NULL, "SYSCLK" },
+ { "IN1BR", NULL, "SYSCLK" },
+ { "IN2A", NULL, "SYSCLK" },
+ { "IN2B", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+
+ { "SLIMRX3", NULL, "Slim2 Playback" },
+ { "SLIMRX4", NULL, "Slim2 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "A", "IN1AL" },
+ { "IN1R Mux", "A", "IN1AR" },
+ { "IN1L Mux", "B", "IN1BL" },
+ { "IN1R Mux", "B", "IN1BR" },
+
+ { "IN2 Mux", "A", "IN2A" },
+ { "IN2 Mux", "B", "IN2B" },
+
+ { "IN1L PGA", NULL, "IN1L Mux" },
+ { "IN1R PGA", NULL, "IN1R Mux" },
+ { "IN2 PGA", NULL, "IN2 Mux" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"),
+
+ ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"),
+ ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"),
+
+ ARIZONA_MUX_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MUX_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MUX_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MUX_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC1 Loopback", "HPOUTL", "OUT1L" },
+ { "AEC1 Loopback", "HPOUTR", "OUT1R" },
+ { "AEC2 Loopback", "HPOUTL", "OUT1L" },
+ { "AEC2 Loopback", "HPOUTR", "OUT1R" },
+ { "HPOUTL", NULL, "OUT1L" },
+ { "HPOUTR", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "LINEOUTL", "OUT2L" },
+ { "AEC1 Loopback", "LINEOUTR", "OUT2R" },
+ { "AEC2 Loopback", "LINEOUTL", "OUT2L" },
+ { "AEC2 Loopback", "LINEOUTR", "OUT2R" },
+ { "LINEOUTL", NULL, "OUT2L" },
+ { "LINEOUTR", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "EPOUT", "OUT3" },
+ { "AEC2 Loopback", "EPOUT", "OUT3" },
+ { "EPOUT", NULL, "OUT3" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKOUTR", "OUT4R" },
+ { "AEC2 Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "SPDIF", NULL, "SPD1" },
+
+ { "AEC1 Loopback", "SPKDATL", "OUT5L" },
+ { "AEC1 Loopback", "SPKDATR", "OUT5R" },
+ { "AEC2 Loopback", "SPKDATL", "OUT5L" },
+ { "AEC2 Loopback", "SPKDATR", "OUT5R" },
+ { "SPKDATL", NULL, "OUT5L" },
+ { "SPKDATR", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+};
+
+#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8998_dai[] = {
+ {
+ .name = "wm8998-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8998-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM8998_FLL1:
+ return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout);
+ case WM8998_FLL2:
+ return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout);
+ case WM8998_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref,
+ Fout);
+ case WM8998_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wm8998_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ priv->core.arizona->dapm = dapm;
+
+ arizona_init_spk(codec);
+ arizona_init_gpio(codec);
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ return 0;
+}
+
+static int wm8998_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM8998_DIG_VU 0x0200
+
+static unsigned int wm8998_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct regmap *wm8998_get_regmap(struct device *dev)
+{
+ struct wm8998_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
+ .probe = wm8998_codec_probe,
+ .remove = wm8998_codec_remove,
+ .get_regmap = wm8998_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8998_set_fll,
+
+ .controls = wm8998_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8998_snd_controls),
+ .dapm_widgets = wm8998_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets),
+ .dapm_routes = wm8998_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes),
+};
+
+static int wm8998_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm8998_priv *wm8998;
+ int i;
+
+ wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
+ GFP_KERNEL);
+ if (!wm8998)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8998);
+
+ wm8998->core.arizona = arizona;
+ wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
+
+ for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++)
+ wm8998->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm8998->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm8998->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++)
+ arizona_init_dai(&wm8998->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm8998_digital_vu[i],
+ WM8998_DIG_VU, WM8998_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
+ wm8998_dai, ARRAY_SIZE(wm8998_dai));
+}
+
+static int wm8998_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm8998_codec_driver = {
+ .driver = {
+ .name = "wm8998-codec",
+ },
+ .probe = wm8998_probe,
+ .remove = wm8998_remove,
+};
+
+module_platform_driver(wm8998_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8998 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:wm8998-codec");
diff --git a/kernel/sound/soc/codecs/wm8998.h b/kernel/sound/soc/codecs/wm8998.h
new file mode 100644
index 000000000..1e8647252
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8998.h
@@ -0,0 +1,23 @@
+/*
+ * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8998_H
+#define _WM8998_H
+
+#include "arizona.h"
+
+#define WM8998_FLL1 1
+#define WM8998_FLL2 2
+#define WM8998_FLL1_REFCLK 3
+#define WM8998_FLL2_REFCLK 4
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm9081.c b/kernel/sound/soc/codecs/wm9081.c
index 13a3f335e..ccb3b1513 100644
--- a/kernel/sound/soc/codecs/wm9081.c
+++ b/kernel/sound/soc/codecs/wm9081.c
@@ -30,7 +30,7 @@
#include <sound/wm9081.h>
#include "wm9081.h"
-static struct reg_default wm9081_reg[] = {
+static const struct reg_default wm9081_reg[] = {
{ 2, 0x00B9 }, /* R2 - Analogue Lineout */
{ 3, 0x00B9 }, /* R3 - Analogue Speaker PGA */
{ 4, 0x0001 }, /* R4 - VMID Control */
@@ -243,13 +243,12 @@ static int wm9081_reset(struct regmap *map)
static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0);
static const DECLARE_TLV_DB_SCALE(drc_out_tlv, -2250, 75, 0);
static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
-static unsigned int drc_max_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(drc_max_tlv,
0, 0, TLV_DB_SCALE_ITEM(1200, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
- 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -300, 50, 0);
@@ -838,7 +837,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 +897,6 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1380,7 +1377,6 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
static struct i2c_driver wm9081_i2c_driver = {
.driver = {
.name = "wm9081",
- .owner = THIS_MODULE,
},
.probe = wm9081_i2c_probe,
.remove = wm9081_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm9090.c b/kernel/sound/soc/codecs/wm9090.c
index 60d243c90..5d737290f 100644
--- a/kernel/sound/soc/codecs/wm9090.c
+++ b/kernel/sound/soc/codecs/wm9090.c
@@ -162,23 +162,20 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec)
dev_err(codec->dev, "Timed out waiting for DC Servo\n");
}
-static const unsigned int in_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+static const DECLARE_TLV_DB_RANGE(in_tlv,
0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
1, 3, TLV_DB_SCALE_ITEM(-350, 350, 0),
- 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0),
-};
-static const unsigned int mix_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+ 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0)
+);
+static const DECLARE_TLV_DB_RANGE(mix_tlv,
0, 2, TLV_DB_SCALE_ITEM(-1200, 300, 0),
- 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
-static const unsigned int spkboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(spkboost_tlv,
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
- 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
-};
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0)
+);
static const struct snd_kcontrol_new wm9090_controls[] = {
SOC_SINGLE_TLV("IN1A Volume", WM9090_IN1_LINE_INPUT_A_VOLUME, 0, 6, 0,
@@ -425,7 +422,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 +493,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 +512,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -638,7 +633,6 @@ MODULE_DEVICE_TABLE(i2c, wm9090_id);
static struct i2c_driver wm9090_i2c_driver = {
.driver = {
.name = "wm9090",
- .owner = THIS_MODULE,
},
.probe = wm9090_i2c_probe,
.remove = wm9090_i2c_remove,
diff --git a/kernel/sound/soc/codecs/wm9705.c b/kernel/sound/soc/codecs/wm9705.c
index 5cc457ef8..744842c76 100644
--- a/kernel/sound/soc/codecs/wm9705.c
+++ b/kernel/sound/soc/codecs/wm9705.c
@@ -22,6 +22,9 @@
#include "wm9705.h"
+#define WM9705_VENDOR_ID 0x574d4c05
+#define WM9705_VENDOR_ID_MASK 0xffffffff
+
/*
* WM9705 register cache
*/
@@ -293,21 +296,6 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
}
};
-static int wm9705_reset(struct snd_soc_codec *codec)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
-
- if (soc_ac97_ops->reset) {
- soc_ac97_ops->reset(ac97);
- if (ac97_read(codec, 0) == wm9705_reg[0])
- return 0; /* Success */
- }
-
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
-
- return -EIO;
-}
-
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
{
@@ -324,7 +312,8 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
int i, ret;
u16 *cache = codec->reg_cache;
- ret = wm9705_reset(codec);
+ ret = snd_ac97_reset(ac97, true, WM9705_VENDOR_ID,
+ WM9705_VENDOR_ID_MASK);
if (ret < 0)
return ret;
@@ -342,30 +331,17 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
static int wm9705_soc_probe(struct snd_soc_codec *codec)
{
struct snd_ac97 *ac97;
- int ret = 0;
- ac97 = snd_soc_alloc_ac97_codec(codec);
+ ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID,
+ WM9705_VENDOR_ID_MASK);
if (IS_ERR(ac97)) {
- ret = PTR_ERR(ac97);
dev_err(codec->dev, "Failed to register AC97 codec\n");
- return ret;
+ return PTR_ERR(ac97);
}
- ret = wm9705_reset(codec);
- if (ret)
- goto err_put_device;
-
- ret = device_add(&ac97->dev);
- if (ret)
- goto err_put_device;
-
snd_soc_codec_set_drvdata(codec, ac97);
return 0;
-
-err_put_device:
- put_device(&ac97->dev);
- return ret;
}
static int wm9705_soc_remove(struct snd_soc_codec *codec)
diff --git a/kernel/sound/soc/codecs/wm9712.c b/kernel/sound/soc/codecs/wm9712.c
index 98c9525bd..488a92224 100644
--- a/kernel/sound/soc/codecs/wm9712.c
+++ b/kernel/sound/soc/codecs/wm9712.c
@@ -23,6 +23,9 @@
#include <sound/tlv.h>
#include "wm9712.h"
+#define WM9712_VENDOR_ID 0x574d4c12
+#define WM9712_VENDOR_ID_MASK 0xffffffff
+
struct wm9712_priv {
struct snd_ac97 *ac97;
unsigned int hp_mixer[2];
@@ -610,43 +613,21 @@ 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;
}
-static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
-{
- struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
-
- if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(wm9712->ac97);
- if (ac97_read(codec, 0) == wm9712_reg[0])
- return 1;
- }
-
- soc_ac97_ops->reset(wm9712->ac97);
- if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(wm9712->ac97);
- if (ac97_read(codec, 0) != wm9712_reg[0])
- goto err;
- return 0;
-
-err:
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
- return -EIO;
-}
-
static int wm9712_soc_resume(struct snd_soc_codec *codec)
{
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
- ret = wm9712_reset(codec, 1);
+ ret = snd_ac97_reset(wm9712->ac97, true, WM9712_VENDOR_ID,
+ WM9712_VENDOR_ID_MASK);
if (ret < 0)
return ret;
- 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 */
@@ -664,31 +645,20 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ int ret;
- wm9712->ac97 = snd_soc_alloc_ac97_codec(codec);
+ wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID,
+ WM9712_VENDOR_ID_MASK);
if (IS_ERR(wm9712->ac97)) {
ret = PTR_ERR(wm9712->ac97);
dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
return ret;
}
- ret = wm9712_reset(codec, 0);
- if (ret < 0)
- goto err_put_device;
-
- ret = device_add(&wm9712->ac97->dev);
- if (ret)
- goto err_put_device;
-
/* set alc mux to none */
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
return 0;
-
-err_put_device:
- put_device(&wm9712->ac97->dev);
- return ret;
}
static int wm9712_soc_remove(struct snd_soc_codec *codec)
diff --git a/kernel/sound/soc/codecs/wm9713.c b/kernel/sound/soc/codecs/wm9713.c
index 79552953e..4083a5130 100644
--- a/kernel/sound/soc/codecs/wm9713.c
+++ b/kernel/sound/soc/codecs/wm9713.c
@@ -29,6 +29,9 @@
#include "wm9713.h"
+#define WM9713_VENDOR_ID 0x574d4c13
+#define WM9713_VENDOR_ID_MASK 0xffffffff
+
struct wm9713_priv {
struct snd_ac97 *ac97;
u32 pll_in; /* PLL input frequency */
@@ -116,11 +119,10 @@ SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0);
-static unsigned int mic_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(mic_tlv,
0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
- 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+ 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv),
@@ -1054,8 +1056,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,
@@ -1123,28 +1125,6 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
},
};
-int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
-{
- struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
-
- if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(wm9713->ac97);
- if (ac97_read(codec, 0) == wm9713_reg[0])
- return 1;
- }
-
- soc_ac97_ops->reset(wm9713->ac97);
- if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(wm9713->ac97);
- if (ac97_read(codec, 0) != wm9713_reg[0]) {
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
- return -EIO;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm9713_reset);
-
static int wm9713_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -1171,7 +1151,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;
}
@@ -1197,11 +1176,12 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
int i, ret;
u16 *cache = codec->reg_cache;
- ret = wm9713_reset(codec, 1);
+ ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID,
+ WM9713_VENDOR_ID_MASK);
if (ret < 0)
return ret;
- 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)
@@ -1223,32 +1203,18 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
static int wm9713_soc_probe(struct snd_soc_codec *codec)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
- int ret = 0, reg;
+ int reg;
- wm9713->ac97 = snd_soc_alloc_ac97_codec(codec);
+ wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID,
+ WM9713_VENDOR_ID_MASK);
if (IS_ERR(wm9713->ac97))
return PTR_ERR(wm9713->ac97);
- /* do a cold reset for the controller and then try
- * a warm reset followed by an optional cold reset for codec */
- wm9713_reset(codec, 0);
- ret = wm9713_reset(codec, 1);
- if (ret < 0)
- goto err_put_device;
-
- ret = device_add(&wm9713->ac97->dev);
- if (ret)
- goto err_put_device;
-
/* unmute the adc - move to kcontrol */
reg = ac97_read(codec, AC97_CD) & 0x7fff;
ac97_write(codec, AC97_CD, reg);
return 0;
-
-err_put_device:
- put_device(&wm9713->ac97->dev);
- return ret;
}
static int wm9713_soc_remove(struct snd_soc_codec *codec)
diff --git a/kernel/sound/soc/codecs/wm9713.h b/kernel/sound/soc/codecs/wm9713.h
index 793da863a..53df11b1f 100644
--- a/kernel/sound/soc/codecs/wm9713.h
+++ b/kernel/sound/soc/codecs/wm9713.h
@@ -45,6 +45,4 @@
#define WM9713_DAI_AC97_AUX 1
#define WM9713_DAI_PCM_VOICE 2
-int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
-
#endif
diff --git a/kernel/sound/soc/codecs/wm_adsp.c b/kernel/sound/soc/codecs/wm_adsp.c
index d01c20954..0bb415a28 100644
--- a/kernel/sound/soc/codecs/wm_adsp.c
+++ b/kernel/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/kernel/sound/soc/codecs/wm_adsp.h b/kernel/sound/soc/codecs/wm_adsp.h
index a4f6b64de..2d117cf0e 100644
--- a/kernel/sound/soc/codecs/wm_adsp.h
+++ b/kernel/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;
+ int fw_ver;
bool running;
- struct regulator *dvfs;
-
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/kernel/sound/soc/codecs/wm_hubs.c b/kernel/sound/soc/codecs/wm_hubs.c
index 8366e1965..624b3b9cb 100644
--- a/kernel/sound/soc/codecs/wm_hubs.c
+++ b/kernel/sound/soc/codecs/wm_hubs.c
@@ -38,11 +38,10 @@ static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
-static const unsigned int spkboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(spkboost_tlv,
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
- 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
-};
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0)
+);
static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
static const char *speaker_ref_text[] = {
@@ -1116,7 +1115,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 +1159,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/kernel/sound/soc/codecs/wmfw.h b/kernel/sound/soc/codecs/wmfw.h
index ef163360a..7613d60d6 100644
--- a/kernel/sound/soc/codecs/wmfw.h
+++ b/kernel/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/kernel/sound/soc/davinci/davinci-i2s.c b/kernel/sound/soc/davinci/davinci-i2s.c
index 56cb4d956..ec98548a5 100644
--- a/kernel/sound/soc/davinci/davinci-i2s.c
+++ b/kernel/sound/soc/davinci/davinci-i2s.c
@@ -651,23 +651,15 @@ static const struct snd_soc_component_driver davinci_i2s_component = {
static int davinci_i2s_probe(struct platform_device *pdev)
{
struct davinci_mcbsp_dev *dev;
- struct resource *mem, *ioarea, *res;
+ struct resource *mem, *res;
+ void __iomem *io_base;
int *dma;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -ENODEV;
- }
-
- ioarea = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem),
- pdev->name);
- if (!ioarea) {
- dev_err(&pdev->dev, "McBSP region already claimed\n");
- return -EBUSY;
- }
+ io_base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev),
GFP_KERNEL);
@@ -679,12 +671,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
return -ENODEV;
clk_enable(dev->clk);
- dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!dev->base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto err_release_clk;
- }
+ dev->base = io_base;
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
diff --git a/kernel/sound/soc/davinci/davinci-mcasp.c b/kernel/sound/soc/davinci/davinci-mcasp.c
index 23c91fa65..2ccb8bccc 100644
--- a/kernel/sound/soc/davinci/davinci-mcasp.c
+++ b/kernel/sound/soc/davinci/davinci-mcasp.c
@@ -80,12 +80,13 @@ struct davinci_mcasp {
/* McASP specific data */
int tdm_slots;
+ u32 tdm_mask[2];
+ int slot_width;
u8 op_mode;
u8 num_serializer;
u8 *serial_dir;
u8 version;
u8 bclk_div;
- u16 bclk_lrclk_ratio;
int streams;
u32 irq_request[2];
int dma_request[2];
@@ -107,6 +108,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,
@@ -221,8 +223,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp)
/* wait for XDATA to be cleared */
cnt = 0;
- while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
- ~XRDATA) && (cnt < 100000))
+ while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) &&
+ (cnt < 100000))
cnt++;
/* Release TX state machine */
@@ -555,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
mcasp->bclk_div = div;
break;
- case 2: /* BCLK/LRCLK ratio */
- mcasp->bclk_lrclk_ratio = div;
+ case 2: /*
+ * BCLK/LRCLK ratio descries how many bit-clock cycles
+ * fit into one frame. The clock ratio is given for a
+ * full period of data (for I2S format both left and
+ * right channels), so it has to be divided by number
+ * of tdm-slots (for I2S - divided by 2).
+ * Instead of storing this ratio, we calculate a new
+ * tdm_slot width by dividing the the ratio by the
+ * number of configured tdm slots.
+ */
+ mcasp->slot_width = div / mcasp->tdm_slots;
+ if (div % mcasp->tdm_slots)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+ __func__, div, mcasp->tdm_slots);
break;
default:
@@ -595,12 +610,92 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
return 0;
}
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
+ int serializers)
+{
+ struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
+ unsigned int *list = (unsigned int *) cl->list;
+ int slots = mcasp->tdm_slots;
+ int i, count = 0;
+
+ if (mcasp->tdm_mask[stream])
+ slots = hweight32(mcasp->tdm_mask[stream]);
+
+ for (i = 2; i <= slots; i++)
+ list[count++] = i;
+
+ for (i = 2; i <= serializers; i++)
+ list[count++] = i*slots;
+
+ cl->count = count;
+
+ return 0;
+}
+
+static int davinci_mcasp_set_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, SNDRV_PCM_STREAM_PLAYBACK,
+ tx_serializers);
+ if (ret)
+ return ret;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
+ rx_serializers);
+
+ return ret;
+}
+
+
+static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(mcasp->dev,
+ "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(mcasp->dev,
+ "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+ tx_mask, rx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (slot_width &&
+ (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
+ dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ mcasp->tdm_slots = slots;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+ mcasp->slot_width = slot_width;
+
+ return davinci_mcasp_set_ch_constraints(mcasp);
+}
+
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int word_length)
+ int sample_width)
{
u32 fmt;
- u32 tx_rotate = (word_length / 4) & 0x7;
- u32 mask = (1ULL << word_length) - 1;
+ u32 tx_rotate = (sample_width / 4) & 0x7;
+ u32 mask = (1ULL << sample_width) - 1;
+ u32 slot_width = sample_width;
+
/*
* For captured data we should not rotate, inversion and masking is
* enoguh to get the data to the right position:
@@ -613,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
u32 rx_rotate = 0;
/*
- * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
- * callback, take it into account here. That allows us to for example
- * send 32 bits per channel to the codec, while only 16 of them carry
- * audio payload.
- * The clock ratio is given for a full period of data (for I2S format
- * both left and right channels), so it has to be divided by number of
- * tdm-slots (for I2S - divided by 2).
+ * Setting the tdm slot width either with set_clkdiv() or
+ * set_tdm_slot() allows us to for example send 32 bits per
+ * channel to the codec, while only 16 of them carry audio
+ * payload.
*/
- if (mcasp->bclk_lrclk_ratio) {
- u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
-
+ if (mcasp->slot_width) {
/*
- * When we have more bclk then it is needed for the data, we
- * need to use the rotation to move the received samples to have
- * correct alignment.
+ * When we have more bclk then it is needed for the
+ * data, we need to use the rotation to move the
+ * received samples to have correct alignment.
*/
- rx_rotate = (slot_length - word_length) / 4;
- word_length = slot_length;
+ slot_width = mcasp->slot_width;
+ rx_rotate = (slot_width - sample_width) / 4;
}
/* mapping of the XSSZ bit-field as described in the datasheet */
- fmt = (word_length >> 1) - 1;
+ fmt = (slot_width >> 1) - 1;
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
@@ -662,7 +752,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
u8 rx_ser = 0;
u8 slots = mcasp->tdm_slots;
u8 max_active_serializers = (channels + slots - 1) / slots;
- int active_serializers, numevt, n;
+ int active_serializers, numevt;
u32 reg;
/* Default configuration */
if (mcasp->version < MCASP_VERSION_3)
@@ -685,6 +775,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) {
@@ -742,9 +834,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
* The number of words for numevt need to be in steps of active
* serializers.
*/
- n = numevt % active_serializers;
- if (n)
- numevt += (active_serializers - n);
+ numevt = (numevt / active_serializers) * active_serializers;
+
while (period_words % numevt && numevt > 0)
numevt -= active_serializers;
if (numevt <= 0)
@@ -774,33 +865,58 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
/*
* If more than one serializer is needed, then use them with
- * their specified tdm_slots count. Otherwise, one serializer
- * can cope with the transaction using as many slots as channels
- * in the stream, requires channels symmetry
+ * all the specified tdm_slots. Otherwise, one serializer can
+ * cope with the transaction using just as many slots as there
+ * are channels in the stream.
*/
- active_serializers = (channels + total_slots - 1) / total_slots;
- if (active_serializers == 1)
- active_slots = channels;
- else
- active_slots = total_slots;
-
- for (i = 0; i < active_slots; i++)
- mask |= (1 << i);
+ if (mcasp->tdm_mask[stream]) {
+ active_slots = hweight32(mcasp->tdm_mask[stream]);
+ active_serializers = (channels + active_slots - 1) /
+ active_slots;
+ if (active_serializers == 1) {
+ active_slots = channels;
+ for (i = 0; i < total_slots; i++) {
+ if ((1 << i) & mcasp->tdm_mask[stream]) {
+ mask |= (1 << i);
+ if (--active_slots <= 0)
+ break;
+ }
+ }
+ }
+ } else {
+ active_serializers = (channels + total_slots - 1) / total_slots;
+ if (active_serializers == 1)
+ active_slots = channels;
+ else
+ active_slots = total_slots;
+ for (i = 0; i < active_slots; i++)
+ mask |= (1 << i);
+ }
mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port)
busel = TXSEL;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(total_slots), FSXMOD(0x1FF));
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(total_slots), FSRMOD(0x1FF));
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(total_slots), FSRMOD(0x1FF));
+ /*
+ * If McASP is set to be TX/RX synchronous and the playback is
+ * not running already we need to configure the TX slots in
+ * order to have correct FSX on the bus
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
+ }
return 0;
}
@@ -915,15 +1031,15 @@ 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;
+ if (mcasp->slot_width)
+ sbits = mcasp->slot_width;
- 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 +1140,39 @@ 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;
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
+ 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,20 +1182,21 @@ 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 sbits = snd_pcm_format_width(i);
int ppm;
- davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
+ davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
+ &ppm);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set(&nfmt, i);
count++;
@@ -1079,51 +1204,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)
{
@@ -1132,6 +1218,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
&mcasp->ruledata[substream->stream];
u32 max_channels = 0;
int i, dir;
+ int tdm_slots = mcasp->tdm_slots;
+
+ if (mcasp->tdm_mask[substream->stream])
+ tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
mcasp->substreams[substream->stream] = substream;
@@ -1152,7 +1242,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
max_channels++;
}
ruledata->serializers = max_channels;
- max_channels *= mcasp->tdm_slots;
+ max_channels *= tdm_slots;
/*
* If the already active stream has less channels than the calculated
* limnit based on the seirializers * tdm_slots, we need to use that as
@@ -1162,11 +1252,26 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
*/
if (mcasp->channels && mcasp->channels < max_channels)
max_channels = mcasp->channels;
+ /*
+ * But we can always allow channels upto the amount of
+ * the available tdm_slots.
+ */
+ if (max_channels < tdm_slots)
+ max_channels = tdm_slots;
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
2, max_channels);
+ snd_pcm_hw_constraint_list(substream->runtime,
+ 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mcasp->chconstr[substream->stream]);
+
+ if (mcasp->slot_width)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ 8, mcasp->slot_width);
+
/*
* If we rely on implicit BCLK divider setting we should
* set constraints based on what we can provide.
@@ -1180,24 +1285,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;
}
@@ -1227,6 +1322,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
.set_fmt = davinci_mcasp_set_dai_fmt,
.set_clkdiv = davinci_mcasp_set_clkdiv,
.set_sysclk = davinci_mcasp_set_sysclk,
+ .set_tdm_slot = davinci_mcasp_set_tdm_slot,
};
static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
@@ -1341,6 +1437,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
.ops = &davinci_mcasp_dai_ops,
.symmetric_samplebits = 1,
+ .symmetric_rates = 1,
},
{
.name = "davinci-mcasp.1",
@@ -1556,10 +1653,53 @@ nodata:
return pdata;
}
+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;
- struct resource *mem, *ioarea, *res, *dat;
+ struct resource *mem, *res, *dat;
struct davinci_mcasp_pdata *pdata;
struct davinci_mcasp *mcasp;
char *irq_name;
@@ -1594,22 +1734,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
}
}
- ioarea = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem), pdev->name);
- if (!ioarea) {
- dev_err(&pdev->dev, "Audio region already claimed\n");
- return -EBUSY;
- }
+ mcasp->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(mcasp->base))
+ return PTR_ERR(mcasp->base);
pm_runtime_enable(&pdev->dev);
- mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!mcasp->base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto err;
- }
-
mcasp->op_mode = pdata->op_mode;
/* sanity check for tdm slots parameter */
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
@@ -1641,7 +1771,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "common");
if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common\n",
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common",
dev_name(&pdev->dev));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
davinci_mcasp_common_irq_handler,
@@ -1658,7 +1788,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "rx");
if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n",
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx",
dev_name(&pdev->dev));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
davinci_mcasp_rx_irq_handler,
@@ -1673,7 +1803,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "tx");
if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n",
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx",
dev_name(&pdev->dev));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
davinci_mcasp_tx_irq_handler,
@@ -1739,6 +1869,31 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
}
+ /* Allocate memory for long enough list for all possible
+ * scenarios. Maximum number tdm slots is 32 and there cannot
+ * be more serializers than given in the configuration. The
+ * serializer directions could be taken into account, but it
+ * would make code much more complex and save only couple of
+ * bytes.
+ */
+ mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
+ devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (32 + mcasp->num_serializer - 2),
+ GFP_KERNEL);
+
+ mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
+ devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (32 + mcasp->num_serializer - 2),
+ GFP_KERNEL);
+
+ if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
+ !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list)
+ return -ENOMEM;
+
+ ret = davinci_mcasp_set_ch_constraints(mcasp);
+ if (ret)
+ goto err;
+
dev_set_drvdata(&pdev->dev, mcasp);
mcasp_reparent_fck(pdev);
@@ -1750,27 +1905,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/kernel/sound/soc/davinci/davinci-mcasp.h b/kernel/sound/soc/davinci/davinci-mcasp.h
index 79dc51118..a3be108a8 100644
--- a/kernel/sound/soc/davinci/davinci-mcasp.h
+++ b/kernel/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/kernel/sound/soc/davinci/davinci-vcif.c b/kernel/sound/soc/davinci/davinci-vcif.c
index fabd05f24..c77d92187 100644
--- a/kernel/sound/soc/davinci/davinci-vcif.c
+++ b/kernel/sound/soc/davinci/davinci-vcif.c
@@ -231,8 +231,9 @@ static int davinci_vcif_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
- ret = snd_soc_register_component(&pdev->dev, &davinci_vcif_component,
- &davinci_vcif_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &davinci_vcif_component,
+ &davinci_vcif_dai, 1);
if (ret != 0) {
dev_err(&pdev->dev, "could not register dai\n");
return ret;
@@ -241,23 +242,14 @@ static int davinci_vcif_probe(struct platform_device *pdev)
ret = edma_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
- snd_soc_unregister_component(&pdev->dev);
return ret;
}
return 0;
}
-static int davinci_vcif_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_component(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver davinci_vcif_driver = {
.probe = davinci_vcif_probe,
- .remove = davinci_vcif_remove,
.driver = {
.name = "davinci-vcif",
},
diff --git a/kernel/sound/soc/dwc/designware_i2s.c b/kernel/sound/soc/dwc/designware_i2s.c
index a3e97b46b..6e6a70c5c 100644
--- a/kernel/sound/soc/dwc/designware_i2s.c
+++ b/kernel/sound/soc/dwc/designware_i2s.c
@@ -131,23 +131,32 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < 4; i++)
- i2s_write_reg(dev->i2s_base, TOR(i), 0);
+ i2s_read_reg(dev->i2s_base, TOR(i));
} else {
for (i = 0; i < 4; i++)
- i2s_write_reg(dev->i2s_base, ROR(i), 0);
+ i2s_read_reg(dev->i2s_base, ROR(i));
}
}
static void i2s_start(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream)
{
-
+ u32 i, irq;
i2s_write_reg(dev->i2s_base, IER, 1);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < 4; i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
+ }
i2s_write_reg(dev->i2s_base, ITER, 1);
- else
+ } else {
+ for (i = 0; i < 4; i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
+ }
i2s_write_reg(dev->i2s_base, IRER, 1);
+ }
i2s_write_reg(dev->i2s_base, CER, 1);
}
@@ -273,23 +282,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
config->sample_rate = params_rate(params);
- if (dev->i2s_clk_cfg) {
- ret = dev->i2s_clk_cfg(config);
- if (ret < 0) {
- dev_err(dev->dev, "runtime audio clk config fail\n");
- return ret;
- }
- } else {
- u32 bitclk = config->sample_rate * config->data_width * 2;
-
- ret = clk_set_rate(dev->clk, bitclk);
- if (ret) {
- dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
- ret);
- return ret;
+ if (dev->capability & DW_I2S_MASTER) {
+ if (dev->i2s_clk_cfg) {
+ ret = dev->i2s_clk_cfg(config);
+ if (ret < 0) {
+ dev_err(dev->dev, "runtime audio clk config fail\n");
+ return ret;
+ }
+ } else {
+ u32 bitclk = config->sample_rate *
+ config->data_width * 2;
+
+ ret = clk_set_rate(dev->clk, bitclk);
+ if (ret) {
+ dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+ ret);
+ return ret;
+ }
}
}
-
return 0;
}
@@ -339,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if (dev->capability & DW_I2S_SLAVE)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ if (dev->capability & DW_I2S_MASTER)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ ret = -EINVAL;
+ break;
+ default:
+ dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static struct snd_soc_dai_ops dw_i2s_dai_ops = {
.startup = dw_i2s_startup,
.shutdown = dw_i2s_shutdown,
.hw_params = dw_i2s_hw_params,
.prepare = dw_i2s_prepare,
.trigger = dw_i2s_trigger,
+ .set_fmt = dw_i2s_set_fmt,
};
static const struct snd_soc_component_driver dw_i2s_component = {
@@ -357,7 +399,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- clk_disable(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable(dev->clk);
return 0;
}
@@ -365,7 +408,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- clk_enable(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_enable(dev->clk);
return 0;
}
@@ -443,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
dw_i2s_dai->capture.rates = rates;
}
+ if (COMP1_MODE_EN(comp1)) {
+ dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+ dev->capability |= DW_I2S_MASTER;
+ } else {
+ dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+ dev->capability |= DW_I2S_SLAVE;
+ }
+
return 0;
}
@@ -529,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
struct resource *res;
int ret;
struct snd_soc_dai_driver *dw_i2s_dai;
+ const char *clk_id;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
@@ -550,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev)
return PTR_ERR(dev->i2s_base);
dev->dev = &pdev->dev;
+
if (pdata) {
+ dev->capability = pdata->cap;
+ clk_id = NULL;
ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
- if (ret < 0)
- return ret;
+ } else {
+ clk_id = "i2sclk";
+ ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ }
+ if (ret < 0)
+ return ret;
- dev->capability = pdata->cap;
- dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
- if (!dev->i2s_clk_cfg) {
- dev_err(&pdev->dev, "no clock configure method\n");
- return -ENODEV;
+ if (dev->capability & DW_I2S_MASTER) {
+ if (pdata) {
+ dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+ if (!dev->i2s_clk_cfg) {
+ dev_err(&pdev->dev, "no clock configure method\n");
+ return -ENODEV;
+ }
}
+ dev->clk = devm_clk_get(&pdev->dev, clk_id);
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- } else {
- ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+
+ ret = clk_prepare_enable(dev->clk);
if (ret < 0)
return ret;
-
- dev->clk = devm_clk_get(&pdev->dev, "i2sclk");
}
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
-
- ret = clk_prepare_enable(dev->clk);
- if (ret < 0)
- return ret;
dev_set_drvdata(&pdev->dev, dev);
ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
@@ -597,7 +653,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
return 0;
err_clk_disable:
- clk_disable_unprepare(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(dev->clk);
return ret;
}
@@ -605,7 +662,8 @@ static int dw_i2s_remove(struct platform_device *pdev)
{
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
- clk_disable_unprepare(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(dev->clk);
return 0;
}
diff --git a/kernel/sound/soc/fsl/Kconfig b/kernel/sound/soc/fsl/Kconfig
index 19c302b0d..14dfdee05 100644
--- a/kernel/sound/soc/fsl/Kconfig
+++ b/kernel/sound/soc/fsl/Kconfig
@@ -283,6 +283,8 @@ config SND_SOC_IMX_MC13783
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
depends on OF && I2C
+ # enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m:
+ depends on SND_AC97_CODEC || SND_AC97_CODEC=n
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_ESAI
diff --git a/kernel/sound/soc/fsl/eukrea-tlv320.c b/kernel/sound/soc/fsl/eukrea-tlv320.c
index e1aa3834b..883087f2b 100644
--- a/kernel/sound/soc/fsl/eukrea-tlv320.c
+++ b/kernel/sound/soc/fsl/eukrea-tlv320.c
@@ -182,7 +182,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
);
} else {
if (np) {
- /* The eukrea,asoc-tlv320 driver was explicitely
+ /* The eukrea,asoc-tlv320 driver was explicitly
* requested (through the device tree).
*/
dev_err(&pdev->dev,
diff --git a/kernel/sound/soc/fsl/fsl-asoc-card.c b/kernel/sound/soc/fsl/fsl-asoc-card.c
index de4388710..1b05d1c5d 100644
--- a/kernel/sound/soc/fsl/fsl-asoc-card.c
+++ b/kernel/sound/soc/fsl/fsl-asoc-card.c
@@ -14,6 +14,9 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+#include <sound/ac97_codec.h>
+#endif
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -23,6 +26,7 @@
#include "../codecs/sgtl5000.h"
#include "../codecs/wm8962.h"
+#include "../codecs/wm8960.h"
#define RX 0
#define TX 1
@@ -114,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
+static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv)
+{
+ return priv->dai_fmt == SND_SOC_DAIFMT_AC97;
+}
+
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -132,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
* set_bias_level(), bypass the remaining settings in hw_params().
* Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
*/
- if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
+ if ((priv->card.set_bias_level &&
+ priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) ||
+ fsl_asoc_card_is_ac97(priv))
return 0;
/* Specific configurations of DAIs starts from here */
@@ -299,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
ext_port--;
/*
- * Use asynchronous mode (6 wires) for all cases.
+ * Use asynchronous mode (6 wires) for all cases except AC97.
* If only 4 wires are needed, just set SSI into
* synchronous mode and enable 4 PADs in IOMUX.
*/
@@ -345,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
default:
- return -EINVAL;
+ if (!fsl_asoc_card_is_ac97(priv))
+ return -EINVAL;
+ }
+
+ if (fsl_asoc_card_is_ac97(priv)) {
+ int_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_TFSDIR;
}
/* Asynchronous mode can not be set along with RCLKDIR */
- ret = imx_audmux_v2_configure_port(int_port, 0,
- IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
- if (ret) {
- dev_err(dev, "audmux internal port setup failed\n");
- return ret;
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ unsigned int pdcr =
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port);
+
+ ret = imx_audmux_v2_configure_port(int_port, 0,
+ pdcr);
+ if (ret) {
+ dev_err(dev, "audmux internal port setup failed\n");
+ return ret;
+ }
}
ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
@@ -363,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
return ret;
}
- ret = imx_audmux_v2_configure_port(ext_port, 0,
- IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
- if (ret) {
- dev_err(dev, "audmux external port setup failed\n");
- return ret;
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ unsigned int pdcr =
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port);
+
+ ret = imx_audmux_v2_configure_port(ext_port, 0,
+ pdcr);
+ if (ret) {
+ dev_err(dev, "audmux external port setup failed\n");
+ return ret;
+ }
}
ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
@@ -388,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
struct device *dev = card->dev;
int ret;
+ if (fsl_asoc_card_is_ac97(priv)) {
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+ struct snd_soc_codec *codec = card->rtd[0].codec;
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * Use slots 3/4 for S/PDIF so SSI won't try to enable
+ * other slots and send some samples there
+ * due to SLOTREQ bits for S/PDIF received from codec
+ */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4);
+#endif
+
+ return 0;
+ }
+
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
if (ret) {
@@ -406,7 +454,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
struct i2c_client *codec_dev;
- struct clk *codec_clk;
+ const char *codec_dai_name;
u32 width;
int ret;
@@ -418,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Give a chance to old DT binding */
if (!cpu_np)
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
- codec_np = of_parse_phandle(np, "audio-codec", 0);
- if (!cpu_np || !codec_np) {
- dev_err(&pdev->dev, "phandle missing or invalid\n");
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
@@ -432,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto fail;
}
- codec_dev = of_find_i2c_device_by_node(codec_np);
- if (!codec_dev) {
- dev_err(&pdev->dev, "failed to find codec platform device\n");
- ret = -EINVAL;
- goto fail;
- }
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (codec_np)
+ codec_dev = of_find_i2c_device_by_node(codec_np);
+ else
+ codec_dev = NULL;
asrc_np = of_parse_phandle(np, "audio-asrc", 0);
if (asrc_np)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
- codec_clk = clk_get(&codec_dev->dev, NULL);
- if (!IS_ERR(codec_clk)) {
- priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
- clk_put(codec_clk);
+ if (codec_dev) {
+ struct clk *codec_clk = clk_get(&codec_dev->dev, NULL);
+
+ if (!IS_ERR(codec_clk)) {
+ priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
+ }
}
/* Default sample rate and format, will be updated in hw_params() */
@@ -459,6 +508,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Diversify the card configurations */
if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
+ codec_dai_name = "cs42888";
priv->card.set_bias_level = NULL;
priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
@@ -467,17 +517,36 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.slot_width = 32;
priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
+ codec_dai_name = "sgtl5000";
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
+ codec_dai_name = "wm8962";
priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
priv->codec_priv.pll_id = WM8962_FLL;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
+ codec_dai_name = "wm8960-hifi";
+ priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
+ priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
+ priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
+ codec_dai_name = "ac97-hifi";
+ priv->card.set_bias_level = NULL;
+ priv->dai_fmt = SND_SOC_DAIFMT_AC97;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto asrc_fail;
+ }
+
+ if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
+ dev_err(&pdev->dev, "failed to find codec device\n");
+ ret = -EINVAL;
+ goto asrc_fail;
}
/* Common settings for corresponding Freescale CPU DAI driver */
@@ -496,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
}
- sprintf(priv->name, "%s-audio", codec_dev->name);
+ snprintf(priv->name, sizeof(priv->name), "%s-audio",
+ fsl_asoc_card_is_ac97(priv) ? "ac97" :
+ codec_dev->name);
/* Initialize sound card */
priv->pdev = pdev;
@@ -520,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Normal DAI Link */
priv->dai_link[0].cpu_of_node = cpu_np;
- priv->dai_link[0].codec_of_node = codec_np;
- priv->dai_link[0].codec_dai_name = codec_dev->name;
+ priv->dai_link[0].codec_dai_name = codec_dai_name;
+
+ if (!fsl_asoc_card_is_ac97(priv))
+ priv->dai_link[0].codec_of_node = codec_np;
+ else {
+ u32 idx;
+
+ ret = of_property_read_u32(cpu_np, "cell-index", &idx);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "cannot get CPU index property\n");
+ goto asrc_fail;
+ }
+
+ priv->dai_link[0].codec_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "ac97-codec.%u",
+ (unsigned int)idx);
+ }
+
priv->dai_link[0].platform_of_node = cpu_np;
priv->dai_link[0].dai_fmt = priv->dai_fmt;
priv->card.num_links = 1;
@@ -530,8 +619,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* DPCM DAI Links only if ASRC exsits */
priv->dai_link[1].cpu_of_node = asrc_np;
priv->dai_link[1].platform_of_node = asrc_np;
- priv->dai_link[2].codec_dai_name = codec_dev->name;
+ priv->dai_link[2].codec_dai_name = codec_dai_name;
priv->dai_link[2].codec_of_node = codec_np;
+ priv->dai_link[2].codec_name =
+ priv->dai_link[0].codec_name;
priv->dai_link[2].cpu_of_node = cpu_np;
priv->dai_link[2].dai_fmt = priv->dai_fmt;
priv->card.num_links = 3;
@@ -567,19 +658,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_fail:
of_node_put(asrc_np);
-fail:
of_node_put(codec_np);
+fail:
of_node_put(cpu_np);
return ret;
}
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-ac97", },
{ .compatible = "fsl,imx-audio-cs42888", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
+ { .compatible = "fsl,imx-audio-wm8960", },
{}
};
+MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
static struct platform_driver fsl_asoc_card_driver = {
.probe = fsl_asoc_card_probe,
diff --git a/kernel/sound/soc/fsl/fsl_asrc.c b/kernel/sound/soc/fsl/fsl_asrc.c
index c068494ba..9f087d4f7 100644
--- a/kernel/sound/soc/fsl/fsl_asrc.c
+++ b/kernel/sound/soc/fsl/fsl_asrc.c
@@ -931,14 +931,29 @@ static int fsl_asrc_probe(struct platform_device *pdev)
static int fsl_asrc_runtime_resume(struct device *dev)
{
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
- int i;
+ int i, ret;
- clk_prepare_enable(asrc_priv->mem_clk);
- clk_prepare_enable(asrc_priv->ipg_clk);
- for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
- clk_prepare_enable(asrc_priv->asrck_clk[i]);
+ ret = clk_prepare_enable(asrc_priv->mem_clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(asrc_priv->ipg_clk);
+ if (ret)
+ goto disable_mem_clk;
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
+ ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
+ if (ret)
+ goto disable_asrck_clk;
+ }
return 0;
+
+disable_asrck_clk:
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(asrc_priv->asrck_clk[i]);
+ clk_disable_unprepare(asrc_priv->ipg_clk);
+disable_mem_clk:
+ clk_disable_unprepare(asrc_priv->mem_clk);
+ return ret;
}
static int fsl_asrc_runtime_suspend(struct device *dev)
diff --git a/kernel/sound/soc/fsl/fsl_dma.c b/kernel/sound/soc/fsl/fsl_dma.c
index 93d7e56c6..ccadefcee 100644
--- a/kernel/sound/soc/fsl/fsl_dma.c
+++ b/kernel/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/kernel/sound/soc/fsl/fsl_esai.c b/kernel/sound/soc/fsl/fsl_esai.c
index 5c7597191..59f234e51 100644
--- a/kernel/sound/soc/fsl/fsl_esai.c
+++ b/kernel/sound/soc/fsl/fsl_esai.c
@@ -517,7 +517,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
u32 bclk, mask, val;
int ret;
- /* Override slot_width if being specifially set */
+ /* Override slot_width if being specifically set */
if (esai_priv->slot_width)
slot_width = esai_priv->slot_width;
@@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = {
.name = "fsl-esai",
};
+static const struct reg_default fsl_esai_reg_defaults[] = {
+ {0x8, 0x00000000},
+ {0x10, 0x00000000},
+ {0x18, 0x00000000},
+ {0x98, 0x00000000},
+ {0xd0, 0x00000000},
+ {0xd4, 0x00000000},
+ {0xd8, 0x00000000},
+ {0xdc, 0x00000000},
+ {0xe0, 0x00000000},
+ {0xe4, 0x0000ffff},
+ {0xe8, 0x0000ffff},
+ {0xec, 0x0000ffff},
+ {0xf0, 0x0000ffff},
+ {0xf8, 0x00000000},
+ {0xfc, 0x00000000},
+};
+
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
}
}
+static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ESAI_ETDR:
+ case REG_ESAI_ERDR:
+ case REG_ESAI_ESR:
+ case REG_ESAI_TFSR:
+ case REG_ESAI_RFSR:
+ case REG_ESAI_TX0:
+ case REG_ESAI_TX1:
+ case REG_ESAI_TX2:
+ case REG_ESAI_TX3:
+ case REG_ESAI_TX4:
+ case REG_ESAI_TX5:
+ case REG_ESAI_RX0:
+ case REG_ESAI_RX1:
+ case REG_ESAI_RX2:
+ case REG_ESAI_RX3:
+ case REG_ESAI_SAISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = {
.val_bits = 32,
.max_register = REG_ESAI_PCRC,
+ .reg_defaults = fsl_esai_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),
.readable_reg = fsl_esai_readable_reg,
+ .volatile_reg = fsl_esai_volatile_reg,
.writeable_reg = fsl_esai_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static int fsl_esai_probe(struct platform_device *pdev)
@@ -839,7 +886,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
return ret;
}
- ret = imx_pcm_dma_init(pdev);
+ ret = imx_pcm_dma_init(pdev, IMX_ESAI_DMABUF_SIZE);
if (ret)
dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
@@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
+#ifdef CONFIG_PM_SLEEP
+static int fsl_esai_suspend(struct device *dev)
+{
+ struct fsl_esai *esai = dev_get_drvdata(dev);
+
+ regcache_cache_only(esai->regmap, true);
+ regcache_mark_dirty(esai->regmap);
+
+ return 0;
+}
+
+static int fsl_esai_resume(struct device *dev)
+{
+ struct fsl_esai *esai = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(esai->regmap, false);
+
+ /* FIFO reset for safety */
+ regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+ regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+ ret = regcache_sync(esai->regmap);
+ if (ret)
+ return ret;
+
+ /* FIFO reset done */
+ regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+ regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_esai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume)
+};
+
static struct platform_driver fsl_esai_driver = {
.probe = fsl_esai_probe,
.driver = {
.name = "fsl-esai-dai",
+ .pm = &fsl_esai_pm_ops,
.of_match_table = fsl_esai_dt_ids,
},
};
diff --git a/kernel/sound/soc/fsl/fsl_sai.c b/kernel/sound/soc/fsl/fsl_sai.c
index ec79c3d5e..08b460ba0 100644
--- a/kernel/sound/soc/fsl/fsl_sai.c
+++ b/kernel/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 const unsigned int fsl_sai_rates[] = {
+ 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000
+};
+
+static const 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)
{
@@ -334,7 +454,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
+ sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
@@ -384,6 +505,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+
+ /*
+ * For sai master mode, after several open/close sai,
+ * there will be no frame clock, and can't recover
+ * anymore. Add software reset to fix this issue.
+ * This is a hardware bug, and will be fix in the
+ * next sai version.
+ */
+ if (!sai->is_slave_mode) {
+ /* Software Reset for both Tx and Rx */
+ regmap_write(sai->regmap,
+ FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap,
+ FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ }
}
break;
default:
@@ -410,7 +549,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 +570,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 +606,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,
@@ -509,6 +656,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case FSL_SAI_TCSR:
+ case FSL_SAI_RCSR:
case FSL_SAI_TFR:
case FSL_SAI_RFR:
case FSL_SAI_TDR:
@@ -553,6 +702,7 @@ static const struct regmap_config fsl_sai_regmap_config = {
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
};
static int fsl_sai_probe(struct platform_device *pdev)
@@ -600,8 +750,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",
@@ -662,10 +813,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
return ret;
if (sai->sai_on_imx)
- return imx_pcm_dma_init(pdev);
+ return imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE);
else
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
- 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[] = {
@@ -673,11 +823,42 @@ static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,imx6sx-sai", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_sai_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_suspend(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, true);
+ regcache_mark_dirty(sai->regmap);
+
+ return 0;
+}
+
+static int fsl_sai_resume(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, false);
+ regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ msleep(1);
+ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ return regcache_sync(sai->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
.driver = {
.name = "fsl-sai",
+ .pm = &fsl_sai_pm_ops,
.of_match_table = fsl_sai_ids,
},
};
diff --git a/kernel/sound/soc/fsl/fsl_sai.h b/kernel/sound/soc/fsl/fsl_sai.h
index 34667209b..b95fbc3f6 100644
--- a/kernel/sound/soc/fsl/fsl_sai.h
+++ b/kernel/sound/soc/fsl/fsl_sai.h
@@ -13,7 +13,8 @@
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
- SNDRV_PCM_FMTBIT_S24_LE)
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
/* SAI Register Map Register */
#define FSL_SAI_TCSR 0x00 /* SAI Transmit Control */
@@ -45,7 +46,7 @@
#define FSL_SAI_xFR(tx) (tx ? FSL_SAI_TFR : FSL_SAI_RFR)
#define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
-/* SAI Transmit/Recieve Control Register */
+/* SAI Transmit/Receive Control Register */
#define FSL_SAI_CSR_TERE BIT(31)
#define FSL_SAI_CSR_FR BIT(25)
#define FSL_SAI_CSR_SR BIT(24)
@@ -67,25 +68,27 @@
#define FSL_SAI_CSR_FRIE BIT(8)
#define FSL_SAI_CSR_FRDE BIT(0)
-/* SAI Transmit and Recieve Configuration 1 Register */
+/* SAI Transmit and Receive Configuration 1 Register */
#define FSL_SAI_CR1_RFW_MASK 0x1f
-/* SAI Transmit and Recieve Configuration 2 Register */
+/* SAI Transmit and Receive Configuration 2 Register */
#define FSL_SAI_CR2_SYNC BIT(30)
-#define FSL_SAI_CR2_MSEL_MASK (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 */
+/* SAI Transmit and Receive Configuration 3 Register */
#define FSL_SAI_CR3_TRCE BIT(16)
#define FSL_SAI_CR3_WDFL(x) (x)
#define FSL_SAI_CR3_WDFL_MASK 0x1f
-/* SAI Transmit and Recieve Configuration 4 Register */
+/* SAI Transmit and Receive Configuration 4 Register */
#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16)
#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16)
#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8)
@@ -95,7 +98,7 @@
#define FSL_SAI_CR4_FSP BIT(1)
#define FSL_SAI_CR4_FSD_MSTR BIT(0)
-/* SAI Transmit and Recieve Configuration 5 Register */
+/* SAI Transmit and Receive Configuration 5 Register */
#define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24)
#define FSL_SAI_CR5_WNW_MASK (0x1f << 24)
#define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16)
@@ -120,7 +123,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 +135,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/kernel/sound/soc/fsl/fsl_spdif.c b/kernel/sound/soc/fsl/fsl_spdif.c
index 91eb3aef7..3d59bb671 100644
--- a/kernel/sound/soc/fsl/fsl_spdif.c
+++ b/kernel/sound/soc/fsl/fsl_spdif.c
@@ -108,6 +108,8 @@ struct fsl_spdif_priv {
struct clk *sysclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
+ /* regcache for SRPC */
+ u32 regcache_srpc;
};
/* DPLL locked and lock loss interrupt handler */
@@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
struct regmap *regmap = spdif_priv->regmap;
u32 val, cycle = 1000;
+ regcache_cache_bypass(regmap, true);
+
regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
/*
@@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
regmap_read(regmap, REG_SPDIF_SCR, &val);
} while ((val & SCR_SOFT_RESET) && cycle--);
+ regcache_cache_bypass(regmap, false);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+
if (cycle)
return 0;
else
@@ -417,11 +425,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;
@@ -456,7 +462,8 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct platform_device *pdev = spdif_priv->pdev;
struct regmap *regmap = spdif_priv->regmap;
- u32 scr, mask, i;
+ u32 scr, mask;
+ int i;
int ret;
/* Reset module and interrupts only for first initialization */
@@ -484,13 +491,18 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
SCR_TXFIFO_FSEL_MASK;
- for (i = 0; i < SPDIF_TXRATE_MAX; i++)
- clk_prepare_enable(spdif_priv->txclk[i]);
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+ ret = clk_prepare_enable(spdif_priv->txclk[i]);
+ if (ret)
+ goto disable_txclk;
+ }
} else {
scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
- clk_prepare_enable(spdif_priv->rxclk);
+ ret = clk_prepare_enable(spdif_priv->rxclk);
+ if (ret)
+ goto err;
}
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
@@ -499,6 +511,9 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
return 0;
+disable_txclk:
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(spdif_priv->txclk[i]);
err:
clk_disable_unprepare(spdif_priv->coreclk);
@@ -709,7 +724,7 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
return ret;
}
-/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+/* Q-subcode information. The byte size is SPDIF_UBITS_SIZE/8 */
static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -741,7 +756,7 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
return ret;
}
-/* Valid bit infomation */
+/* Valid bit information */
static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -769,7 +784,7 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
return 0;
}
-/* DPLL lock infomation */
+/* DPLL lock information */
static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -990,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
};
/* FSL SPDIF REGMAP */
+static const struct reg_default fsl_spdif_reg_defaults[] = {
+ {0x0, 0x00000400},
+ {0x4, 0x00000000},
+ {0xc, 0x00000000},
+ {0x34, 0x00000000},
+ {0x38, 0x00000000},
+ {0x50, 0x00020f00},
+};
static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
{
@@ -1015,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
}
}
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIS:
+ case REG_SPDIF_SRL:
+ case REG_SPDIF_SRR:
+ case REG_SPDIF_SRCSH:
+ case REG_SPDIF_SRCSL:
+ case REG_SPDIF_SRU:
+ case REG_SPDIF_SRQ:
+ case REG_SPDIF_STL:
+ case REG_SPDIF_STR:
+ case REG_SPDIF_SRFM:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -1040,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = {
.val_bits = 32,
.max_register = REG_SPDIF_STC,
+ .reg_defaults = fsl_spdif_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults),
.readable_reg = fsl_spdif_readable_reg,
+ .volatile_reg = fsl_spdif_volatile_reg,
.writeable_reg = fsl_spdif_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
@@ -1060,7 +1107,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
@@ -1257,13 +1304,45 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret;
}
- ret = imx_pcm_dma_init(pdev);
+ ret = imx_pcm_dma_init(pdev, IMX_SPDIF_DMABUF_SIZE);
if (ret)
dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int fsl_spdif_suspend(struct device *dev)
+{
+ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+ regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
+ &spdif_priv->regcache_srpc);
+
+ regcache_cache_only(spdif_priv->regmap, true);
+ regcache_mark_dirty(spdif_priv->regmap);
+
+ return 0;
+}
+
+static int fsl_spdif_resume(struct device *dev)
+{
+ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdif_priv->regmap, false);
+
+ regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
+ SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+ spdif_priv->regcache_srpc);
+
+ return regcache_sync(spdif_priv->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_spdif_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
+};
+
static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx35-spdif", },
{ .compatible = "fsl,vf610-spdif", },
@@ -1275,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = {
.driver = {
.name = "fsl-spdif-dai",
.of_match_table = fsl_spdif_dt_ids,
+ .pm = &fsl_spdif_pm,
},
.probe = fsl_spdif_probe,
};
diff --git a/kernel/sound/soc/fsl/fsl_ssi.c b/kernel/sound/soc/fsl/fsl_ssi.c
index 0d4880421..95d239230 100644
--- a/kernel/sound/soc/fsl/fsl_ssi.c
+++ b/kernel/sound/soc/fsl/fsl_ssi.c
@@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val {
struct fsl_ssi_reg_val rx;
struct fsl_ssi_reg_val tx;
};
+
+static const struct reg_default fsl_ssi_reg_defaults[] = {
+ {0x10, 0x00000000},
+ {0x18, 0x00003003},
+ {0x1c, 0x00000200},
+ {0x20, 0x00000200},
+ {0x24, 0x00040000},
+ {0x28, 0x00040000},
+ {0x38, 0x00000000},
+ {0x48, 0x00000000},
+ {0x4c, 0x00000000},
+ {0x54, 0x00000000},
+ {0x58, 0x00000000},
+};
+
+static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_SACCEN:
+ case CCSR_SSI_SACCDIS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_STX0:
+ case CCSR_SSI_STX1:
+ case CCSR_SSI_SRX0:
+ case CCSR_SSI_SRX1:
+ case CCSR_SSI_SISR:
+ case CCSR_SSI_SFCSR:
+ case CCSR_SSI_SACADD:
+ case CCSR_SSI_SACDAT:
+ case CCSR_SSI_SATAG:
+ case CCSR_SSI_SACCST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_SRX0:
+ case CCSR_SSI_SRX1:
+ case CCSR_SSI_SACCST:
+ return false;
+ default:
+ return true;
+ }
+}
+
static const struct regmap_config fsl_ssi_regconfig = {
.max_register = CCSR_SSI_SACCDIS,
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .reg_defaults = fsl_ssi_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+ .readable_reg = fsl_ssi_readable_reg,
+ .volatile_reg = fsl_ssi_volatile_reg,
+ .writeable_reg = fsl_ssi_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
struct fsl_ssi_soc_data {
@@ -156,7 +219,7 @@ struct fsl_ssi_soc_data {
*
* @dbg_stats: Debugging statistics
*
- * @soc: SoC specifc data
+ * @soc: SoC specific data
*/
struct fsl_ssi_private {
struct regmap *regs;
@@ -176,6 +239,9 @@ struct fsl_ssi_private {
unsigned int baudclk_streams;
unsigned int bitclk_freq;
+ /*regcache for SFCSR*/
+ u32 regcache_sfcsr;
+
/* DMA params */
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -249,7 +315,8 @@ MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
{
- return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97);
+ return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+ SND_SOC_DAIFMT_AC97;
}
static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
@@ -633,7 +700,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
sub *= 100000;
do_div(sub, freq);
- if (sub < savesub) {
+ if (sub < savesub && !(i == 0 && psr == 0 && div2 == 0)) {
baudrate = tmprate;
savesub = sub;
pm = i;
@@ -900,14 +967,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
break;
default:
- return -EINVAL;
+ if (!fsl_ssi_is_ac97(ssi_private))
+ return -EINVAL;
}
stcr |= strcr;
srcr |= strcr;
- if (ssi_private->cpu_dai_drv.symmetric_rates) {
- /* Need to clear RXDIR when using SYNC mode */
+ if (ssi_private->cpu_dai_drv.symmetric_rates
+ || fsl_ssi_is_ac97(ssi_private)) {
+ /* Need to clear RXDIR when using SYNC or AC97 mode */
srcr &= ~CCSR_SSI_SRCR_RXDIR;
scr |= CCSR_SSI_SCR_SYN;
}
@@ -945,7 +1014,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
CCSR_SSI_SCR_TCH_EN);
}
- if (fmt & SND_SOC_DAIFMT_AC97)
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97)
fsl_ssi_setup_ac97(ssi_private);
return 0;
@@ -1101,6 +1170,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.bus_control = true,
+ .probe = fsl_ssi_dai_probe,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -1127,10 +1197,17 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
struct regmap *regs = fsl_ac97_data->regs;
unsigned int lreg;
unsigned int lval;
+ int ret;
if (reg > 0x7f)
return;
+ ret = clk_prepare_enable(fsl_ac97_data->clk);
+ if (ret) {
+ pr_err("ac97 write clk_prepare_enable failed: %d\n",
+ ret);
+ return;
+ }
lreg = reg << 12;
regmap_write(regs, CCSR_SSI_SACADD, lreg);
@@ -1141,6 +1218,8 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK,
CCSR_SSI_SACNT_WR);
udelay(100);
+
+ clk_disable_unprepare(fsl_ac97_data->clk);
}
static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
@@ -1151,6 +1230,14 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
unsigned short val = -1;
u32 reg_val;
unsigned int lreg;
+ int ret;
+
+ ret = clk_prepare_enable(fsl_ac97_data->clk);
+ if (ret) {
+ pr_err("ac97 read clk_prepare_enable failed: %d\n",
+ ret);
+ return -1;
+ }
lreg = (reg & 0x7f) << 12;
regmap_write(regs, CCSR_SSI_SACADD, lreg);
@@ -1162,6 +1249,8 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
regmap_read(regs, CCSR_SSI_SACDAT, &reg_val);
val = (reg_val >> 4) & 0xffff;
+ clk_disable_unprepare(fsl_ac97_data->clk);
+
return val;
}
@@ -1210,7 +1299,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
}
}
- /* For those SLAVE implementations, we ingore non-baudclk cases
+ /* For those SLAVE implementations, we ignore non-baudclk cases
* and, instead, abandon MASTER mode that needs baud clock.
*/
ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
@@ -1257,7 +1346,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
if (ret)
goto error_pcm;
} else {
- ret = imx_pcm_dma_init(pdev);
+ ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
if (ret)
goto error_pcm;
}
@@ -1292,13 +1381,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;
@@ -1327,7 +1409,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
fsl_ac97_data = ssi_private;
- snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+ ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not set AC'97 ops\n");
+ return ret;
+ }
} else {
/* Initialize this copy of the CPU DAI driver structure */
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
@@ -1364,7 +1450,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
/* Are the RX and the TX clocks locked? */
if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
- ssi_private->cpu_dai_drv.symmetric_rates = 1;
+ if (!fsl_ssi_is_ac97(ssi_private))
+ ssi_private->cpu_dai_drv.symmetric_rates = 1;
+
ssi_private->cpu_dai_drv.symmetric_channels = 1;
ssi_private->cpu_dai_drv.symmetric_samplebits = 1;
}
@@ -1441,6 +1529,27 @@ done:
_fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private,
ssi_private->dai_fmt);
+ if (fsl_ssi_is_ac97(ssi_private)) {
+ u32 ssi_idx;
+
+ ret = of_property_read_u32(np, "cell-index", &ssi_idx);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot get SSI index property\n");
+ goto error_sound_card;
+ }
+
+ ssi_private->pdev =
+ platform_device_register_data(NULL,
+ "ac97-codec", ssi_idx, NULL, 0);
+ if (IS_ERR(ssi_private->pdev)) {
+ ret = PTR_ERR(ssi_private->pdev);
+ dev_err(&pdev->dev,
+ "failed to register AC97 codec platform: %d\n",
+ ret);
+ goto error_sound_card;
+ }
+ }
+
return 0;
error_sound_card:
@@ -1465,13 +1574,52 @@ static int fsl_ssi_remove(struct platform_device *pdev)
if (ssi_private->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private);
+ if (fsl_ssi_is_ac97(ssi_private))
+ snd_soc_set_ac97_ops(NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_ssi_suspend(struct device *dev)
+{
+ struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+ struct regmap *regs = ssi_private->regs;
+
+ regmap_read(regs, CCSR_SSI_SFCSR,
+ &ssi_private->regcache_sfcsr);
+
+ regcache_cache_only(regs, true);
+ regcache_mark_dirty(regs);
+
return 0;
}
+static int fsl_ssi_resume(struct device *dev)
+{
+ struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+ struct regmap *regs = ssi_private->regs;
+
+ regcache_cache_only(regs, false);
+
+ regmap_update_bits(regs, CCSR_SSI_SFCSR,
+ CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK |
+ CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK,
+ ssi_private->regcache_sfcsr);
+
+ return regcache_sync(regs);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_ssi_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+};
+
static struct platform_driver fsl_ssi_driver = {
.driver = {
.name = "fsl-ssi-dai",
.of_match_table = fsl_ssi_ids,
+ .pm = &fsl_ssi_pm,
},
.probe = fsl_ssi_probe,
.remove = fsl_ssi_remove,
diff --git a/kernel/sound/soc/fsl/imx-audmux.c b/kernel/sound/soc/fsl/imx-audmux.c
index d9050d946..fc57da341 100644
--- a/kernel/sound/soc/fsl/imx-audmux.c
+++ b/kernel/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/kernel/sound/soc/fsl/imx-mc13783.c b/kernel/sound/soc/fsl/imx-mc13783.c
index 9e6493d4e..bb0459018 100644
--- a/kernel/sound/soc/fsl/imx-mc13783.c
+++ b/kernel/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/kernel/sound/soc/fsl/imx-pcm-dma.c b/kernel/sound/soc/fsl/imx-pcm-dma.c
index 0db94f492..1fc01ed32 100644
--- a/kernel/sound/soc/fsl/imx-pcm-dma.c
+++ b/kernel/sound/soc/fsl/imx-pcm-dma.c
@@ -40,7 +40,7 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
- .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
+ .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
.periods_min = 2,
@@ -52,13 +52,30 @@ static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
.pcm_hardware = &imx_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.compat_filter_fn = filter,
- .prealloc_buffer_size = IMX_SSI_DMABUF_SIZE,
+ .prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
};
-int imx_pcm_dma_init(struct platform_device *pdev)
+int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
{
+ struct snd_dmaengine_pcm_config *config;
+ struct snd_pcm_hardware *pcm_hardware;
+
+ config = devm_kzalloc(&pdev->dev,
+ sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
+ *config = imx_dmaengine_pcm_config;
+ if (size)
+ config->prealloc_buffer_size = size;
+
+ pcm_hardware = devm_kzalloc(&pdev->dev,
+ sizeof(struct snd_pcm_hardware), GFP_KERNEL);
+ *pcm_hardware = imx_pcm_hardware;
+ if (size)
+ pcm_hardware->buffer_bytes_max = size;
+
+ config->pcm_hardware = pcm_hardware;
+
return devm_snd_dmaengine_pcm_register(&pdev->dev,
- &imx_dmaengine_pcm_config,
+ config,
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
diff --git a/kernel/sound/soc/fsl/imx-pcm.h b/kernel/sound/soc/fsl/imx-pcm.h
index c79cb2747..133c4470a 100644
--- a/kernel/sound/soc/fsl/imx-pcm.h
+++ b/kernel/sound/soc/fsl/imx-pcm.h
@@ -20,6 +20,11 @@
*/
#define IMX_SSI_DMABUF_SIZE (64 * 1024)
+#define IMX_DEFAULT_DMABUF_SIZE (64 * 1024)
+#define IMX_SAI_DMABUF_SIZE (64 * 1024)
+#define IMX_SPDIF_DMABUF_SIZE (64 * 1024)
+#define IMX_ESAI_DMABUF_SIZE (256 * 1024)
+
static inline void
imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data,
int dma, enum sdma_peripheral_type peripheral_type)
@@ -39,9 +44,9 @@ struct imx_pcm_fiq_params {
};
#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA)
-int imx_pcm_dma_init(struct platform_device *pdev);
+int imx_pcm_dma_init(struct platform_device *pdev, size_t size);
#else
-static inline int imx_pcm_dma_init(struct platform_device *pdev)
+static inline int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
{
return -ENODEV;
}
diff --git a/kernel/sound/soc/fsl/imx-spdif.c b/kernel/sound/soc/fsl/imx-spdif.c
index 33da26a12..a407e833c 100644
--- a/kernel/sound/soc/fsl/imx-spdif.c
+++ b/kernel/sound/soc/fsl/imx-spdif.c
@@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
static struct platform_driver imx_spdif_driver = {
.driver = {
.name = "imx-spdif",
+ .pm = &snd_soc_pm_ops,
.of_match_table = imx_spdif_dt_ids,
},
.probe = imx_spdif_audio_probe,
diff --git a/kernel/sound/soc/fsl/imx-ssi.c b/kernel/sound/soc/fsl/imx-ssi.c
index 461ce27b8..b95132e2f 100644
--- a/kernel/sound/soc/fsl/imx-ssi.c
+++ b/kernel/sound/soc/fsl/imx-ssi.c
@@ -95,7 +95,8 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* data on rising edge of bclk, frame low 1clk before data */
- strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI |
+ SSI_STCR_TEFS;
scr |= SSI_SCR_NET;
if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
scr &= ~SSI_I2S_MODE_MASK;
@@ -104,33 +105,31 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_LEFT_J:
/* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_DSP_B:
/* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL;
break;
case SND_SOC_DAIFMT_DSP_A:
/* data on rising edge of bclk, frame high 1clk before data */
- strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0 | SSI_STCR_TEFS;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL |
+ SSI_STCR_TEFS;
break;
}
/* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
- strcr |= SSI_STCR_TFSI;
- strcr &= ~SSI_STCR_TSCKP;
+ strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI;
break;
case SND_SOC_DAIFMT_IB_NF:
- strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ strcr ^= SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_NB_IF:
- strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ strcr ^= SSI_STCR_TFSI;
break;
case SND_SOC_DAIFMT_NB_NF:
- strcr &= ~SSI_STCR_TFSI;
- strcr |= SSI_STCR_TSCKP;
break;
}
@@ -603,7 +602,7 @@ static int imx_ssi_probe(struct platform_device *pdev)
ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
- ssi->dma_init = imx_pcm_dma_init(pdev);
+ ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
if (ssi->fiq_init && ssi->dma_init) {
ret = ssi->fiq_init;
diff --git a/kernel/sound/soc/fsl/mpc8610_hpcd.c b/kernel/sound/soc/fsl/mpc8610_hpcd.c
index 9621b9140..6f236f170 100644
--- a/kernel/sound/soc/fsl/mpc8610_hpcd.c
+++ b/kernel/sound/soc/fsl/mpc8610_hpcd.c
@@ -12,11 +12,11 @@
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/fsl/guts.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
-#include <asm/fsl_guts.h>
#include "fsl_dma.h"
#include "fsl_ssi.h"
diff --git a/kernel/sound/soc/fsl/p1022_ds.c b/kernel/sound/soc/fsl/p1022_ds.c
index 71c1a7dc3..747aab060 100644
--- a/kernel/sound/soc/fsl/p1022_ds.c
+++ b/kernel/sound/soc/fsl/p1022_ds.c
@@ -11,12 +11,12 @@
*/
#include <linux/module.h>
+#include <linux/fsl/guts.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
-#include <asm/fsl_guts.h>
#include "fsl_dma.h"
#include "fsl_ssi.h"
diff --git a/kernel/sound/soc/fsl/p1022_rdk.c b/kernel/sound/soc/fsl/p1022_rdk.c
index ee2904842..1dd49e5f9 100644
--- a/kernel/sound/soc/fsl/p1022_rdk.c
+++ b/kernel/sound/soc/fsl/p1022_rdk.c
@@ -18,12 +18,12 @@
*/
#include <linux/module.h>
+#include <linux/fsl/guts.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
-#include <asm/fsl_guts.h>
#include "fsl_dma.h"
#include "fsl_ssi.h"
diff --git a/kernel/sound/soc/generic/simple-card.c b/kernel/sound/soc/generic/simple-card.c
index 33feee9ca..54c332045 100644
--- a/kernel/sound/soc/generic/simple-card.c
+++ b/kernel/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;
@@ -75,16 +76,32 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- 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);
+ if (ret && ret != -ENOTSUPP)
+ goto err;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret && ret != -ENOTSUPP)
+ goto err;
}
+err:
return ret;
}
@@ -134,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
}
if (set->slots) {
- ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
+ ret = snd_soc_dai_set_tdm_slot(dai,
+ set->tx_slot_mask,
+ set->rx_slot_mask,
set->slots,
set->slot_width);
if (ret && ret != -ENOTSUPP) {
@@ -226,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
return ret;
/* Parse TDM slot */
- ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
+ ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
+ &dai->rx_slot_mask,
+ &dai->slots, &dai->slot_width);
if (ret)
return ret;
@@ -307,11 +328,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 +343,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 +360,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 +381,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/kernel/sound/soc/intel/Kconfig b/kernel/sound/soc/intel/Kconfig
index ee03dbdda..d430ef5a4 100644
--- a/kernel/sound/soc/intel/Kconfig
+++ b/kernel/sound/soc/intel/Kconfig
@@ -12,6 +12,7 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM
tristate
+ select SND_SOC_COMPRESS
config SND_SST_IPC
tristate
@@ -26,14 +27,9 @@ config SND_SST_IPC_ACPI
depends on ACPI
config SND_SOC_INTEL_SST
- tristate "ASoC support for Intel(R) Smart Sound Technology"
+ tristate
select SND_SOC_INTEL_SST_ACPI if ACPI
depends on (X86 || COMPILE_TEST)
- depends on DW_DMAC_CORE
- help
- This adds support for Intel(R) Smart Sound Technology (SST).
- Say Y if you have such a device
- If unsure select "N".
config SND_SOC_INTEL_SST_ACPI
tristate
@@ -46,8 +42,9 @@ config SND_SOC_INTEL_BAYTRAIL
config SND_SOC_INTEL_HASWELL_MACH
tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
- depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \
- I2C_DESIGNWARE_PLATFORM
+ depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
+ depends on DW_DMAC_CORE
+ select SND_SOC_INTEL_SST
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT5640
help
@@ -58,7 +55,9 @@ config SND_SOC_INTEL_HASWELL_MACH
config SND_SOC_INTEL_BYT_RT5640_MACH
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
- depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+ depends on X86_INTEL_LPSS && I2C
+ depends on DW_DMAC_CORE
+ select SND_SOC_INTEL_SST
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_RT5640
help
@@ -67,7 +66,9 @@ config SND_SOC_INTEL_BYT_RT5640_MACH
config SND_SOC_INTEL_BYT_MAX98090_MACH
tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
- depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+ depends on X86_INTEL_LPSS && I2C
+ depends on DW_DMAC_CORE
+ select SND_SOC_INTEL_SST
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_MAX98090
help
@@ -76,10 +77,11 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
config SND_SOC_INTEL_BROADWELL_MACH
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
- depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \
+ depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
I2C_DESIGNWARE_PLATFORM
+ depends on DW_DMAC_CORE
+ select SND_SOC_INTEL_SST
select SND_SOC_INTEL_HASWELL
- select SND_COMPRESS_OFFLOAD
select SND_SOC_RT286
help
This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
@@ -112,12 +114,43 @@ 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".
+
+config SND_SOC_INTEL_SKYLAKE
+ tristate
+ select SND_HDA_EXT_CORE
+ select SND_SOC_TOPOLOGY
+ select SND_SOC_INTEL_SST
+
+config SND_SOC_INTEL_SKL_RT286_MACH
+ tristate "ASoC Audio driver for SKL with RT286 I2S mode"
+ depends on X86 && ACPI && I2C
+ select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SKYLAKE
+ select SND_SOC_RT286
+ select SND_SOC_DMIC
+ help
+ This adds support for ASoC machine driver for Skylake platforms
+ with RT286 I2S audio codec.
+ Say Y if you have such a device
+ If unsure select "N".
diff --git a/kernel/sound/soc/intel/Makefile b/kernel/sound/soc/intel/Makefile
index 3853ec2dd..2b45435e6 100644
--- a/kernel/sound/soc/intel/Makefile
+++ b/kernel/sound/soc/intel/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/
# Machine support
-obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/
+obj-$(CONFIG_SND_SOC) += boards/
diff --git a/kernel/sound/soc/intel/atom/sst-atom-controls.c b/kernel/sound/soc/intel/atom/sst-atom-controls.c
index 90aa5c047..d55388e08 100644
--- a/kernel/sound/soc/intel/atom/sst-atom-controls.c
+++ b/kernel/sound/soc/intel/atom/sst-atom-controls.c
@@ -132,7 +132,7 @@ static int sst_send_slot_map(struct sst_data *drv)
sizeof(cmd.header) + cmd.header.length);
}
-int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+static int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
@@ -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,
@@ -1177,7 +1298,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
dev_dbg(dai->dev, "Stream name=%s\n",
dai->playback_widget->name);
w = dai->playback_widget;
- list_for_each_entry(p, &w->sinks, list_source) {
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->connected && !p->connected(w, p->sink))
continue;
@@ -1196,7 +1317,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
dev_dbg(dai->dev, "Stream name=%s\n",
dai->capture_widget->name);
w = dai->capture_widget;
- list_for_each_entry(p, &w->sources, list_sink) {
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
if (p->connected && !p->connected(w, p->sink))
continue;
@@ -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/kernel/sound/soc/intel/atom/sst-atom-controls.h b/kernel/sound/soc/intel/atom/sst-atom-controls.h
index daecc58f2..93de8045d 100644
--- a/kernel/sound/soc/intel/atom/sst-atom-controls.h
+++ b/kernel/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/kernel/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/kernel/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 2fbaf2c75..0487cfaac 100644
--- a/kernel/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/kernel/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -33,7 +33,6 @@
struct sst_device *sst;
static DEFINE_MUTEX(sst_lock);
-extern struct snd_compr_ops sst_platform_compr_ops;
int sst_register_dsp(struct sst_device *dev)
{
@@ -369,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream,
kfree(stream);
}
-static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
- struct snd_pcm_substream *substream)
-{
- struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
- struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
- struct sst_runtime_stream *stream =
- substream->runtime->private_data;
- u32 str_id = stream->stream_info.str_id;
- unsigned int pipe_id;
-
- pipe_id = map[str_id].device_id;
-
- dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
- pipe_id, str_id);
- return pipe_id;
-}
-
static int sst_media_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -434,13 +416,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 +485,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,
};
@@ -489,7 +512,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
{
.name = "compress-cpu-dai",
- .compress_dai = 1,
+ .compress_new = snd_soc_new_compress,
.ops = &sst_compr_dai_ops,
.playback = {
.stream_name = "Compress Playback",
diff --git a/kernel/sound/soc/intel/atom/sst-mfld-platform.h b/kernel/sound/soc/intel/atom/sst-mfld-platform.h
index 9094314be..cb32cc7e5 100644
--- a/kernel/sound/soc/intel/atom/sst-mfld-platform.h
+++ b/kernel/sound/soc/intel/atom/sst-mfld-platform.h
@@ -22,8 +22,10 @@
#define __SST_PLATFORMDRV_H__
#include "sst-mfld-dsp.h"
+#include "sst-atom-controls.h"
extern struct sst_device *sst;
+extern struct snd_compr_ops sst_platform_compr_ops;
#define SST_MONO 1
#define SST_STEREO 2
@@ -175,6 +177,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/kernel/sound/soc/intel/atom/sst/sst_acpi.c b/kernel/sound/soc/intel/atom/sst/sst_acpi.c
index 05f693083..bb19b5801 100644
--- a/kernel/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/kernel/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/kernel/sound/soc/intel/atom/sst/sst_drv_interface.c b/kernel/sound/soc/intel/atom/sst/sst_drv_interface.c
index edc186908..ce689c5af 100644
--- a/kernel/sound/soc/intel/atom/sst/sst_drv_interface.c
+++ b/kernel/sound/soc/intel/atom/sst/sst_drv_interface.c
@@ -151,6 +151,7 @@ static int sst_power_control(struct device *dev, bool state)
usage_count = GET_USAGE_COUNT(dev);
dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
if (ret < 0) {
+ pm_runtime_put_sync(dev);
dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
return ret;
}
@@ -204,8 +205,10 @@ static int sst_cdev_open(struct device *dev,
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
retval = pm_runtime_get_sync(ctx->dev);
- if (retval < 0)
+ if (retval < 0) {
+ pm_runtime_put_sync(ctx->dev);
return retval;
+ }
str_id = sst_get_stream(ctx, str_params);
if (str_id > 0) {
@@ -533,7 +536,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;
@@ -672,8 +675,10 @@ static int sst_send_byte_stream(struct device *dev,
if (NULL == bytes)
return -EINVAL;
ret_val = pm_runtime_get_sync(ctx->dev);
- if (ret_val < 0)
+ if (ret_val < 0) {
+ pm_runtime_put_sync(ctx->dev);
return ret_val;
+ }
ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
sst_pm_runtime_put(ctx);
diff --git a/kernel/sound/soc/intel/atom/sst/sst_ipc.c b/kernel/sound/soc/intel/atom/sst/sst_ipc.c
index 5a2786184..3dc735882 100644
--- a/kernel/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/kernel/sound/soc/intel/atom/sst/sst_ipc.c
@@ -352,10 +352,9 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
* copy from mailbox
**/
if (msg_high.part.large) {
- data = kzalloc(msg_low, GFP_KERNEL);
+ data = kmemdup((void *)msg->mailbox_data, msg_low, GFP_KERNEL);
if (!data)
return;
- memcpy(data, (void *) msg->mailbox_data, msg_low);
/* Copy command id so that we can use to put sst to reset */
dsp_hdr = (struct ipc_dsp_hdr *)data;
cmd_id = dsp_hdr->cmd_id;
diff --git a/kernel/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/kernel/sound/soc/intel/baytrail/sst-baytrail-ipc.c
index a839dbfa5..5bbaa667b 100644
--- a/kernel/sound/soc/intel/baytrail/sst-baytrail-ipc.c
+++ b/kernel/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;
@@ -693,12 +701,17 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
if (byt == NULL)
return -ENOMEM;
+ byt->dev = dev;
+
ipc = &byt->ipc;
ipc->dev = dev;
ipc->ops.tx_msg = byt_tx_msg;
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/kernel/sound/soc/intel/boards/Makefile b/kernel/sound/soc/intel/boards/Makefile
index f8237f004..371c4565c 100644
--- a/kernel/sound/soc/intel/boards/Makefile
+++ b/kernel/sound/soc/intel/boards/Makefile
@@ -5,6 +5,8 @@ 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
+snd-soc-skl_rt286-objs := skl_rt286.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 +15,5 @@ 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
+obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
diff --git a/kernel/sound/soc/intel/boards/broadwell.c b/kernel/sound/soc/intel/boards/broadwell.c
index 8bafaf6ce..3f8a1e10b 100644
--- a/kernel/sound/soc/intel/boards/broadwell.c
+++ b/kernel/sound/soc/intel/boards/broadwell.c
@@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev)
{
broadwell_rt286.dev = &pdev->dev;
- return snd_soc_register_card(&broadwell_rt286);
-}
-
-static int broadwell_audio_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&broadwell_rt286);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
}
static struct platform_driver broadwell_audio = {
.probe = broadwell_audio_probe,
- .remove = broadwell_audio_remove,
.driver = {
.name = "broadwell-audio",
},
diff --git a/kernel/sound/soc/intel/boards/byt-max98090.c b/kernel/sound/soc/intel/boards/byt-max98090.c
index 7ab8cc9fb..d9f81b8d9 100644
--- a/kernel/sound/soc/intel/boards/byt-max98090.c
+++ b/kernel/sound/soc/intel/boards/byt-max98090.c
@@ -126,6 +126,7 @@ static struct snd_soc_dai_link byt_max98090_dais[] = {
static struct snd_soc_card byt_max98090_card = {
.name = "byt-max98090",
+ .owner = THIS_MODULE,
.dai_link = byt_max98090_dais,
.num_links = ARRAY_SIZE(byt_max98090_dais),
.dapm_widgets = byt_max98090_widgets,
diff --git a/kernel/sound/soc/intel/boards/byt-rt5640.c b/kernel/sound/soc/intel/boards/byt-rt5640.c
index ae89b9b96..de9788a3f 100644
--- a/kernel/sound/soc/intel/boards/byt-rt5640.c
+++ b/kernel/sound/soc/intel/boards/byt-rt5640.c
@@ -197,6 +197,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
static struct snd_soc_card byt_rt5640_card = {
.name = "byt-rt5640",
+ .owner = THIS_MODULE,
.dai_link = byt_rt5640_dais,
.num_links = ARRAY_SIZE(byt_rt5640_dais),
.dapm_widgets = byt_rt5640_widgets,
diff --git a/kernel/sound/soc/intel/boards/bytcr_rt5640.c b/kernel/sound/soc/intel/boards/bytcr_rt5640.c
index 7f55d5902..7a5c9a36c 100644
--- a/kernel/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/kernel/sound/soc/intel/boards/bytcr_rt5640.c
@@ -117,20 +117,10 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
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 byt_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops byt_aif1_ops = {
@@ -185,6 +175,7 @@ static struct snd_soc_dai_link byt_dailink[] = {
/* SoC card */
static struct snd_soc_card snd_soc_card_byt = {
.name = "baytrailcraudio",
+ .owner = THIS_MODULE,
.dai_link = byt_dailink,
.num_links = ARRAY_SIZE(byt_dailink),
.dapm_widgets = byt_dapm_widgets,
diff --git a/kernel/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/kernel/sound/soc/intel/boards/cht_bsw_max98090_ti.c
new file mode 100644
index 000000000..4e2fcf188
--- /dev/null
+++ b/kernel/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -0,0 +1,335 @@
+/*
+ * 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"},
+ {"HiFi 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, "HiFi 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_dapm_context *dapm = &jack->card->dapm;
+
+ if (event & SND_JACK_MICROPHONE) {
+ snd_soc_dapm_force_enable_pin(dapm, "SHDN");
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin(dapm, "SHDN");
+ snd_soc_dapm_sync(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 int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 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",
+ .owner = THIS_MODULE,
+ .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/kernel/sound/soc/intel/boards/cht_bsw_rt5645.c b/kernel/sound/soc/intel/boards/cht_bsw_rt5645.c
index 20a28b22e..38d65a352 100644
--- a/kernel/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/kernel/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;
}
@@ -207,20 +235,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
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);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops cht_aif1_ops = {
@@ -239,7 +257,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 +285,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 +293,87 @@ 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",
+ .owner = THIS_MODULE,
.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",
+ .owner = THIS_MODULE,
+ .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/kernel/sound/soc/intel/boards/cht_bsw_rt5672.c b/kernel/sound/soc/intel/boards/cht_bsw_rt5672.c
index 2c9cc5be4..5621ccd92 100644
--- a/kernel/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/kernel/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -222,20 +222,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
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);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops cht_aif1_ops = {
@@ -323,6 +313,7 @@ static int cht_resume_post(struct snd_soc_card *card)
/* SoC card */
static struct snd_soc_card snd_soc_card_cht = {
.name = "cherrytrailcraudio",
+ .owner = THIS_MODULE,
.dai_link = cht_dailink,
.num_links = ARRAY_SIZE(cht_dailink),
.dapm_widgets = cht_dapm_widgets,
diff --git a/kernel/sound/soc/intel/boards/skl_rt286.c b/kernel/sound/soc/intel/boards/skl_rt286.c
new file mode 100644
index 000000000..a73a431bd
--- /dev/null
+++ b/kernel/sound/soc/intel/boards/skl_rt286.c
@@ -0,0 +1,259 @@
+/*
+ * Intel Skylake I2S Machine Driver
+ *
+ * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ * Intel Broadwell Wildcatpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. 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 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/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack skylake_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin skylake_headset_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new skylake_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget skylake_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("DMIC2", NULL),
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route skylake_rt286_map[] = {
+ /* speaker */
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+
+ /* HP jack connectors - unknown if we have jack deteck */
+ {"Headphone Jack", NULL, "HPO Pin"},
+
+ /* other jacks */
+ {"MIC1", NULL, "Mic Jack"},
+
+ /* digital mics */
+ {"DMIC1 Pin", NULL, "DMIC2"},
+ {"DMIC AIF", NULL, "SoC DMIC"},
+
+ /* CODEC BE connections */
+ { "AIF1 Playback", NULL, "ssp0 Tx"},
+ { "ssp0 Tx", NULL, "codec0_out"},
+ { "ssp0 Tx", NULL, "codec1_out"},
+
+ { "codec0_in", NULL, "ssp0 Rx" },
+ { "codec1_in", NULL, "ssp0 Rx" },
+ { "ssp0 Rx", NULL, "AIF1 Capture" },
+
+ { "dmic01_hifi", NULL, "DMIC01 Rx" },
+ { "DMIC01 Rx", NULL, "Capture" },
+
+ { "hif1", NULL, "iDisp Tx"},
+ { "iDisp Tx", NULL, "iDisp_out"},
+
+};
+
+static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &skylake_headset,
+ skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
+
+ if (ret)
+ return ret;
+
+ rt286_mic_detect(codec, &skylake_headset);
+
+ return 0;
+}
+
+
+static int skylake_ssp0_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);
+
+ /* The output is 48KHz, stereo, 16bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int skylake_rt286_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, RT286_SCLK_S_PLL, 24000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_ops skylake_rt286_ops = {
+ .hw_params = skylake_rt286_hw_params,
+};
+
+/* skylake digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link skylake_rt286_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "Skl Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:1f.3",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Skl Audio Capture Port",
+ .stream_name = "Audio Record",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:1f.3",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Skl Audio Reference cap",
+ .stream_name = "refcap",
+ .cpu_dai_name = "Reference Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "SSP0-Codec",
+ .be_id = 0,
+ .cpu_dai_name = "SSP0 Pin",
+ .platform_name = "0000:00:1f.3",
+ .no_pcm = 1,
+ .codec_name = "i2c-INT343A:00",
+ .codec_dai_name = "rt286-aif1",
+ .init = skylake_rt286_codec_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = skylake_ssp0_fixup,
+ .ops = &skylake_rt286_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "dmic01",
+ .be_id = 1,
+ .cpu_dai_name = "DMIC01 Pin",
+ .codec_name = "dmic-codec",
+ .codec_dai_name = "dmic-hifi",
+ .platform_name = "0000:00:1f.3",
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ },
+};
+
+/* skylake audio machine driver for SPT + RT286S */
+static struct snd_soc_card skylake_rt286 = {
+ .name = "skylake-rt286",
+ .owner = THIS_MODULE,
+ .dai_link = skylake_rt286_dais,
+ .num_links = ARRAY_SIZE(skylake_rt286_dais),
+ .controls = skylake_controls,
+ .num_controls = ARRAY_SIZE(skylake_controls),
+ .dapm_widgets = skylake_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
+ .dapm_routes = skylake_rt286_map,
+ .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
+ .fully_routed = true,
+};
+
+static int skylake_audio_probe(struct platform_device *pdev)
+{
+ skylake_rt286.dev = &pdev->dev;
+
+ return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
+}
+
+static struct platform_driver skylake_audio = {
+ .probe = skylake_audio_probe,
+ .driver = {
+ .name = "skl_alc286s_i2s",
+ },
+};
+
+module_platform_driver(skylake_audio)
+
+/* Module information */
+MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
+MODULE_DESCRIPTION("Intel SST Audio for Skylake");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl_alc286s_i2s");
diff --git a/kernel/sound/soc/intel/common/Makefile b/kernel/sound/soc/intel/common/Makefile
index f24154ca4..d9105584c 100644
--- a/kernel/sound/soc/intel/common/Makefile
+++ b/kernel/sound/soc/intel/common/Makefile
@@ -1,7 +1,11 @@
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-dsp-objs := sst-dsp.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
+ifneq ($(CONFIG_DW_DMAC_CORE),)
+snd-soc-sst-dsp-objs += sst-firmware.o
+endif
+
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
diff --git a/kernel/sound/soc/intel/common/sst-acpi.c b/kernel/sound/soc/intel/common/sst-acpi.c
index 42f293f9c..67b6d3d52 100644
--- a/kernel/sound/soc/intel/common/sst-acpi.c
+++ b/kernel/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/kernel/sound/soc/intel/common/sst-dsp-priv.h b/kernel/sound/soc/intel/common/sst-dsp-priv.h
index 396d54510..2151652d3 100644
--- a/kernel/sound/soc/intel/common/sst-dsp-priv.h
+++ b/kernel/sound/soc/intel/common/sst-dsp-priv.h
@@ -22,6 +22,8 @@
#include <linux/interrupt.h>
#include <linux/firmware.h>
+#include "../skylake/skl-sst-dsp.h"
+
struct sst_mem_block;
struct sst_module;
struct sst_fw;
@@ -258,6 +260,8 @@ struct sst_mem_block {
*/
struct sst_dsp {
+ /* Shared for all platforms */
+
/* runtime */
struct sst_dsp_device *sst_dev;
spinlock_t spinlock; /* IPC locking */
@@ -268,10 +272,6 @@ struct sst_dsp {
int irq;
u32 id;
- /* list of free and used ADSP memory blocks */
- struct list_head used_block_list;
- struct list_head free_block_list;
-
/* operations */
struct sst_ops *ops;
@@ -284,6 +284,12 @@ struct sst_dsp {
/* mailbox */
struct sst_mailbox mailbox;
+ /* HSW/Byt data */
+
+ /* list of free and used ADSP memory blocks */
+ struct list_head used_block_list;
+ struct list_head free_block_list;
+
/* SST FW files loaded and their modules */
struct list_head module_list;
struct list_head fw_list;
@@ -299,6 +305,16 @@ struct sst_dsp {
/* DMA FW loading */
struct sst_dma *dma;
bool fw_use_dma;
+
+ /* SKL data */
+
+ /* To allocate CL dma buffers */
+ struct skl_dsp_loader_ops dsp_ops;
+ struct skl_dsp_fw_ops fw_ops;
+ int sst_state;
+ struct skl_cl_dev cl_dev;
+ u32 intr_status;
+ const struct firmware *fw;
};
/* Size optimised DRAM/IRAM memcpy */
diff --git a/kernel/sound/soc/intel/common/sst-dsp.c b/kernel/sound/soc/intel/common/sst-dsp.c
index 64e94212d..c9452e02e 100644
--- a/kernel/sound/soc/intel/common/sst-dsp.c
+++ b/kernel/sound/soc/intel/common/sst-dsp.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/delay.h>
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
@@ -196,6 +197,22 @@ int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
}
EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
+/* This is for registers bits with attribute RWC */
+void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value)
+{
+ unsigned int old, new;
+ u32 ret;
+
+ ret = sst_dsp_shim_read_unlocked(sst, offset);
+
+ old = ret;
+ new = (old & (~mask)) | (value & mask);
+
+ sst_dsp_shim_write_unlocked(sst, offset, new);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced_unlocked);
+
int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
u32 mask, u32 value)
{
@@ -222,6 +239,60 @@ int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
}
EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
+/* This is for registers bits with attribute RWC */
+void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ sst_dsp_shim_update_bits_forced_unlocked(sst, offset, mask, value);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced);
+
+int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
+ u32 target, u32 timeout, char *operation)
+{
+ int time, ret;
+ u32 reg;
+ bool done = false;
+
+ /*
+ * we will poll for couple of ms using mdelay, if not successful
+ * then go to longer sleep using usleep_range
+ */
+
+ /* check if set state successful */
+ for (time = 0; time < 5; time++) {
+ if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) {
+ done = true;
+ break;
+ }
+ mdelay(1);
+ }
+
+ if (done == false) {
+ /* sleeping in 10ms steps so adjust timeout value */
+ timeout /= 10;
+
+ for (time = 0; time < timeout; time++) {
+ if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target)
+ break;
+
+ usleep_range(5000, 10000);
+ }
+ }
+
+ reg = sst_dsp_shim_read_unlocked(ctx, offset);
+ dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
+ (time < timeout) ? "successful" : "timedout");
+ ret = time < timeout ? 0 : -ETIME;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_register_poll);
+
void sst_dsp_dump(struct sst_dsp *sst)
{
if (sst->ops->dump)
@@ -349,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
}
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
{
@@ -413,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst)
sst_dma_free(sst->dma);
}
EXPORT_SYMBOL_GPL(sst_dsp_free);
+#endif
/* Module information */
MODULE_AUTHOR("Liam Girdwood");
diff --git a/kernel/sound/soc/intel/common/sst-dsp.h b/kernel/sound/soc/intel/common/sst-dsp.h
index 96aeb2556..859f0de00 100644
--- a/kernel/sound/soc/intel/common/sst-dsp.h
+++ b/kernel/sound/soc/intel/common/sst-dsp.h
@@ -216,10 +216,12 @@ struct sst_pdata {
void *dsp;
};
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
/* Initialization */
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
void sst_dsp_free(struct sst_dsp *sst);
+#endif
/* SHIM Read / Write */
void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
@@ -230,6 +232,8 @@ void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value);
u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset);
int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value);
+void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value);
/* SHIM Read / Write Unlocked for callers already holding sst lock */
void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value);
@@ -240,6 +244,8 @@ void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value);
u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset);
int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
u64 mask, u64 value);
+void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value);
/* Internal generic low-level SST IO functions - can be overidden */
void sst_shim32_write(void __iomem *addr, u32 offset, u32 value);
@@ -278,6 +284,8 @@ void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes);
+int sst_dsp_register_poll(struct sst_dsp *dsp, u32 offset, u32 mask,
+ u32 expected_value, u32 timeout, char *operation);
/* Debug */
void sst_dsp_dump(struct sst_dsp *sst);
diff --git a/kernel/sound/soc/intel/common/sst-firmware.c b/kernel/sound/soc/intel/common/sst-firmware.c
index ebcca6dc4..1636a1eeb 100644
--- a/kernel/sound/soc/intel/common/sst-firmware.c
+++ b/kernel/sound/soc/intel/common/sst-firmware.c
@@ -26,7 +26,6 @@
#include <linux/acpi.h>
/* supported DMA engine drivers */
-#include <linux/platform_data/dma-dw.h>
#include <linux/dma/dw.h>
#include <asm/page.h>
@@ -169,12 +168,6 @@ err:
return ret;
}
-static struct dw_dma_platform_data dw_pdata = {
- .is_private = 1,
- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
- .chan_priority = CHAN_PRIORITY_ASCENDING,
-};
-
static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
int irq)
{
@@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
return ERR_PTR(err);
chip->dev = dev;
- err = dw_dma_probe(chip, &dw_pdata);
+
+ err = dw_dma_probe(chip, NULL);
if (err)
return ERR_PTR(err);
diff --git a/kernel/sound/soc/intel/common/sst-ipc.c b/kernel/sound/soc/intel/common/sst-ipc.c
index 4b62a5538..a12c7bb08 100644
--- a/kernel/sound/soc/intel/common/sst-ipc.c
+++ b/kernel/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/kernel/sound/soc/intel/common/sst-ipc.h b/kernel/sound/soc/intel/common/sst-ipc.h
index 125ea451a..ceb7e468a 100644
--- a/kernel/sound/soc/intel/common/sst-ipc.h
+++ b/kernel/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/kernel/sound/soc/intel/haswell/sst-haswell-ipc.c b/kernel/sound/soc/intel/haswell/sst-haswell-ipc.c
index 324eceb07..b27f25f70 100644
--- a/kernel/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/kernel/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -302,6 +302,10 @@ struct sst_hsw {
struct sst_hsw_ipc_dx_reply dx;
void *dx_context;
dma_addr_t dx_context_paddr;
+ enum sst_hsw_device_id dx_dev;
+ enum sst_hsw_device_mclk dx_mclk;
+ enum sst_hsw_device_mode dx_mode;
+ u32 dx_clock_divider;
/* boot */
wait_queue_head_t boot_wait;
@@ -1400,10 +1404,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
trace_ipc_request("set device config", dev);
- config.ssp_interface = dev;
- config.clock_frequency = mclk;
- config.mode = mode;
- config.clock_divider = clock_divider;
+ hsw->dx_dev = config.ssp_interface = dev;
+ hsw->dx_mclk = config.clock_frequency = mclk;
+ hsw->dx_mode = config.mode = mode;
+ hsw->dx_clock_divider = config.clock_divider = clock_divider;
if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
config.channels = 4;
else
@@ -1704,10 +1708,10 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
return -EIO;
}
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ /* Set ADSP SSP port settings - sadly the FW does not store SSP port
+ settings as part of the PM context. */
+ ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk,
+ hsw->dx_mode, hsw->dx_clock_divider);
if (ret < 0)
dev_err(dev, "error: SSP re-initialization failed\n");
@@ -2098,6 +2102,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;
@@ -2111,12 +2123,18 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
if (hsw == NULL)
return -ENOMEM;
+ hsw->dev = dev;
+
ipc = &hsw->ipc;
ipc->dev = dev;
ipc->ops.tx_msg = hsw_tx_msg;
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/kernel/sound/soc/intel/haswell/sst-haswell-pcm.c b/kernel/sound/soc/intel/haswell/sst-haswell-pcm.c
index 23ae0400d..1aa819c7e 100644
--- a/kernel/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/kernel/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/kernel/sound/soc/intel/skylake/Makefile b/kernel/sound/soc/intel/skylake/Makefile
new file mode 100644
index 000000000..914b6dab9
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/Makefile
@@ -0,0 +1,10 @@
+snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \
+skl-topology.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
+
+# Skylake IPC Support
+snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
+ skl-sst.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
diff --git a/kernel/sound/soc/intel/skylake/skl-messages.c b/kernel/sound/soc/intel/skylake/skl-messages.c
new file mode 100644
index 000000000..50a109503
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-messages.c
@@ -0,0 +1,918 @@
+/*
+ * skl-message.c - HDA DSP interface for FW registration, Pipe and Module
+ * configurations
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
+ * Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+#include "skl.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-topology.h"
+#include "skl-tplg-interface.h"
+
+static int skl_alloc_dma_buf(struct device *dev,
+ struct snd_dma_buffer *dmab, size_t size)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ if (!bus)
+ return -ENODEV;
+
+ return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, size, dmab);
+}
+
+static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ if (!bus)
+ return -ENODEV;
+
+ bus->io_ops->dma_free_pages(bus, dmab);
+
+ return 0;
+}
+
+#define NOTIFICATION_PARAM_ID 3
+#define NOTIFICATION_MASK 0xf
+
+/* disable notfication for underruns/overruns from firmware module */
+static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+{
+ struct notification_mask mask;
+ struct skl_ipc_large_config_msg msg = {0};
+
+ mask.notify = NOTIFICATION_MASK;
+ mask.enable = enable;
+
+ msg.large_param_id = NOTIFICATION_PARAM_ID;
+ msg.param_data_size = sizeof(mask);
+
+ skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
+}
+
+int skl_init_dsp(struct skl *skl)
+{
+ void __iomem *mmio_base;
+ struct hdac_ext_bus *ebus = &skl->ebus;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ int irq = bus->irq;
+ struct skl_dsp_loader_ops loader_ops;
+ int ret;
+
+ loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
+ loader_ops.free_dma_buf = skl_free_dma_buf;
+
+ /* enable ppcap interrupt */
+ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
+ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
+
+ /* read the BAR of the ADSP MMIO */
+ mmio_base = pci_ioremap_bar(skl->pci, 4);
+ if (mmio_base == NULL) {
+ dev_err(bus->dev, "ioremap error\n");
+ return -ENXIO;
+ }
+
+ ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
+ loader_ops, &skl->skl_sst);
+ if (ret < 0)
+ return ret;
+
+ skl_dsp_enable_notification(skl->skl_sst, false);
+ dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
+
+ return ret;
+}
+
+void skl_free_dsp(struct skl *skl)
+{
+ struct hdac_ext_bus *ebus = &skl->ebus;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl_sst *ctx = skl->skl_sst;
+
+ /* disable ppcap interrupt */
+ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
+
+ skl_sst_dsp_cleanup(bus->dev, ctx);
+ if (ctx->dsp->addr.lpe)
+ iounmap(ctx->dsp->addr.lpe);
+}
+
+int skl_suspend_dsp(struct skl *skl)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret;
+
+ /* if ppcap is not supported return 0 */
+ if (!skl->ebus.ppcap)
+ return 0;
+
+ ret = skl_dsp_sleep(ctx->dsp);
+ if (ret < 0)
+ return ret;
+
+ /* disable ppcap interrupt */
+ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
+ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, false);
+
+ return 0;
+}
+
+int skl_resume_dsp(struct skl *skl)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret;
+
+ /* if ppcap is not supported return 0 */
+ if (!skl->ebus.ppcap)
+ return 0;
+
+ /* enable ppcap interrupt */
+ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
+ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
+
+ ret = skl_dsp_wake(ctx->dsp);
+ if (ret < 0)
+ return ret;
+
+ skl_dsp_enable_notification(skl->skl_sst, false);
+ return ret;
+}
+
+enum skl_bitdepth skl_get_bit_depth(int params)
+{
+ switch (params) {
+ case 8:
+ return SKL_DEPTH_8BIT;
+
+ case 16:
+ return SKL_DEPTH_16BIT;
+
+ case 24:
+ return SKL_DEPTH_24BIT;
+
+ case 32:
+ return SKL_DEPTH_32BIT;
+
+ default:
+ return SKL_DEPTH_INVALID;
+
+ }
+}
+
+static u32 skl_create_channel_map(enum skl_ch_cfg ch_cfg)
+{
+ u32 config;
+
+ switch (ch_cfg) {
+ case SKL_CH_CFG_MONO:
+ config = (0xFFFFFFF0 | SKL_CHANNEL_LEFT);
+ break;
+
+ case SKL_CH_CFG_STEREO:
+ config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_RIGHT << 4));
+ break;
+
+ case SKL_CH_CFG_2_1:
+ config = (0xFFFFF000 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_RIGHT << 4)
+ | (SKL_CHANNEL_LFE << 8));
+ break;
+
+ case SKL_CH_CFG_3_0:
+ config = (0xFFFFF000 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_CENTER << 4)
+ | (SKL_CHANNEL_RIGHT << 8));
+ break;
+
+ case SKL_CH_CFG_3_1:
+ config = (0xFFFF0000 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_CENTER << 4)
+ | (SKL_CHANNEL_RIGHT << 8)
+ | (SKL_CHANNEL_LFE << 12));
+ break;
+
+ case SKL_CH_CFG_QUATRO:
+ config = (0xFFFF0000 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_RIGHT << 4)
+ | (SKL_CHANNEL_LEFT_SURROUND << 8)
+ | (SKL_CHANNEL_RIGHT_SURROUND << 12));
+ break;
+
+ case SKL_CH_CFG_4_0:
+ config = (0xFFFF0000 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_CENTER << 4)
+ | (SKL_CHANNEL_RIGHT << 8)
+ | (SKL_CHANNEL_CENTER_SURROUND << 12));
+ break;
+
+ case SKL_CH_CFG_5_0:
+ config = (0xFFF00000 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_CENTER << 4)
+ | (SKL_CHANNEL_RIGHT << 8)
+ | (SKL_CHANNEL_LEFT_SURROUND << 12)
+ | (SKL_CHANNEL_RIGHT_SURROUND << 16));
+ break;
+
+ case SKL_CH_CFG_5_1:
+ config = (0xFF000000 | SKL_CHANNEL_CENTER
+ | (SKL_CHANNEL_LEFT << 4)
+ | (SKL_CHANNEL_RIGHT << 8)
+ | (SKL_CHANNEL_LEFT_SURROUND << 12)
+ | (SKL_CHANNEL_RIGHT_SURROUND << 16)
+ | (SKL_CHANNEL_LFE << 20));
+ break;
+
+ case SKL_CH_CFG_DUAL_MONO:
+ config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_LEFT << 4));
+ break;
+
+ case SKL_CH_CFG_I2S_DUAL_STEREO_0:
+ config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
+ | (SKL_CHANNEL_RIGHT << 4));
+ break;
+
+ case SKL_CH_CFG_I2S_DUAL_STEREO_1:
+ config = (0xFFFF00FF | (SKL_CHANNEL_LEFT << 8)
+ | (SKL_CHANNEL_RIGHT << 12));
+ break;
+
+ default:
+ config = 0xFFFFFFFF;
+ break;
+
+ }
+
+ return config;
+}
+
+/*
+ * Each module in DSP expects a base module configuration, which consists of
+ * PCM format information, which we calculate in driver and resource values
+ * which are read from widget information passed through topology binary
+ * This is send when we create a module with INIT_INSTANCE IPC msg
+ */
+static void skl_set_base_module_format(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_base_cfg *base_cfg)
+{
+ struct skl_module_fmt *format = &mconfig->in_fmt;
+
+ base_cfg->audio_fmt.number_of_channels = (u8)format->channels;
+
+ base_cfg->audio_fmt.s_freq = format->s_freq;
+ base_cfg->audio_fmt.bit_depth = format->bit_depth;
+ base_cfg->audio_fmt.valid_bit_depth = format->valid_bit_depth;
+ base_cfg->audio_fmt.ch_cfg = format->ch_cfg;
+
+ dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x\n",
+ format->bit_depth, format->valid_bit_depth,
+ format->ch_cfg);
+
+ base_cfg->audio_fmt.channel_map = skl_create_channel_map(
+ base_cfg->audio_fmt.ch_cfg);
+
+ base_cfg->audio_fmt.interleaving = SKL_INTERLEAVING_PER_CHANNEL;
+
+ base_cfg->cps = mconfig->mcps;
+ base_cfg->ibs = mconfig->ibs;
+ base_cfg->obs = mconfig->obs;
+}
+
+/*
+ * Copies copier capabilities into copier module and updates copier module
+ * config size.
+ */
+static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
+ struct skl_cpr_cfg *cpr_mconfig)
+{
+ if (mconfig->formats_config.caps_size == 0)
+ return;
+
+ memcpy(cpr_mconfig->gtw_cfg.config_data,
+ mconfig->formats_config.caps,
+ mconfig->formats_config.caps_size);
+
+ cpr_mconfig->gtw_cfg.config_length =
+ (mconfig->formats_config.caps_size) / 4;
+}
+
+#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF
+/*
+ * Calculate the gatewat settings required for copier module, type of
+ * gateway and index of gateway to use
+ */
+static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_cpr_cfg *cpr_mconfig)
+{
+ union skl_connector_node_id node_id = {0};
+ union skl_ssp_dma_node ssp_node = {0};
+ struct skl_pipe_params *params = mconfig->pipe->p_params;
+
+ switch (mconfig->dev_type) {
+ case SKL_DEVICE_BT:
+ node_id.node.dma_type =
+ (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
+ SKL_DMA_I2S_LINK_OUTPUT_CLASS :
+ SKL_DMA_I2S_LINK_INPUT_CLASS;
+ node_id.node.vindex = params->host_dma_id +
+ (mconfig->vbus_id << 3);
+ break;
+
+ case SKL_DEVICE_I2S:
+ node_id.node.dma_type =
+ (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
+ SKL_DMA_I2S_LINK_OUTPUT_CLASS :
+ SKL_DMA_I2S_LINK_INPUT_CLASS;
+ ssp_node.dma_node.time_slot_index = mconfig->time_slot;
+ ssp_node.dma_node.i2s_instance = mconfig->vbus_id;
+ node_id.node.vindex = ssp_node.val;
+ break;
+
+ case SKL_DEVICE_DMIC:
+ node_id.node.dma_type = SKL_DMA_DMIC_LINK_INPUT_CLASS;
+ node_id.node.vindex = mconfig->vbus_id +
+ (mconfig->time_slot);
+ break;
+
+ case SKL_DEVICE_HDALINK:
+ node_id.node.dma_type =
+ (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
+ SKL_DMA_HDA_LINK_OUTPUT_CLASS :
+ SKL_DMA_HDA_LINK_INPUT_CLASS;
+ node_id.node.vindex = params->link_dma_id;
+ break;
+
+ case SKL_DEVICE_HDAHOST:
+ node_id.node.dma_type =
+ (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
+ SKL_DMA_HDA_HOST_OUTPUT_CLASS :
+ SKL_DMA_HDA_HOST_INPUT_CLASS;
+ node_id.node.vindex = params->host_dma_id;
+ break;
+
+ default:
+ cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+ cpr_mconfig->cpr_feature_mask = 0;
+ return;
+ }
+
+ cpr_mconfig->gtw_cfg.node_id = node_id.val;
+
+ if (SKL_CONN_SOURCE == mconfig->hw_conn_type)
+ cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs;
+ else
+ cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->ibs;
+
+ cpr_mconfig->cpr_feature_mask = 0;
+ cpr_mconfig->gtw_cfg.config_length = 0;
+
+ skl_copy_copier_caps(mconfig, cpr_mconfig);
+}
+
+static void skl_setup_out_format(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_audio_data_format *out_fmt)
+{
+ struct skl_module_fmt *format = &mconfig->out_fmt;
+
+ out_fmt->number_of_channels = (u8)format->channels;
+ out_fmt->s_freq = format->s_freq;
+ out_fmt->bit_depth = format->bit_depth;
+ out_fmt->valid_bit_depth = format->valid_bit_depth;
+ out_fmt->ch_cfg = format->ch_cfg;
+
+ out_fmt->channel_map = skl_create_channel_map(out_fmt->ch_cfg);
+ out_fmt->interleaving = SKL_INTERLEAVING_PER_CHANNEL;
+
+ dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n",
+ out_fmt->number_of_channels, format->s_freq, format->bit_depth);
+}
+
+/*
+ * DSP needs SRC module for frequency conversion, SRC takes base module
+ * configuration and the target frequency as extra parameter passed as src
+ * config
+ */
+static void skl_set_src_format(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_src_module_cfg *src_mconfig)
+{
+ struct skl_module_fmt *fmt = &mconfig->out_fmt;
+
+ skl_set_base_module_format(ctx, mconfig,
+ (struct skl_base_cfg *)src_mconfig);
+
+ src_mconfig->src_cfg = fmt->s_freq;
+}
+
+/*
+ * DSP needs updown module to do channel conversion. updown module take base
+ * module configuration and channel configuration
+ * It also take coefficients and now we have defaults applied here
+ */
+static void skl_set_updown_mixer_format(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_up_down_mixer_cfg *mixer_mconfig)
+{
+ struct skl_module_fmt *fmt = &mconfig->out_fmt;
+ int i = 0;
+
+ skl_set_base_module_format(ctx, mconfig,
+ (struct skl_base_cfg *)mixer_mconfig);
+ mixer_mconfig->out_ch_cfg = fmt->ch_cfg;
+
+ /* Select F/W default coefficient */
+ mixer_mconfig->coeff_sel = 0x0;
+
+ /* User coeff, don't care since we are selecting F/W defaults */
+ for (i = 0; i < UP_DOWN_MIXER_MAX_COEFF; i++)
+ mixer_mconfig->coeff[i] = 0xDEADBEEF;
+}
+
+/*
+ * 'copier' is DSP internal module which copies data from Host DMA (HDA host
+ * dma) or link (hda link, SSP, PDM)
+ * Here we calculate the copier module parameters, like PCM format, output
+ * format, gateway settings
+ * copier_module_config is sent as input buffer with INIT_INSTANCE IPC msg
+ */
+static void skl_set_copier_format(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_cpr_cfg *cpr_mconfig)
+{
+ struct skl_audio_data_format *out_fmt = &cpr_mconfig->out_fmt;
+ struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)cpr_mconfig;
+
+ skl_set_base_module_format(ctx, mconfig, base_cfg);
+
+ skl_setup_out_format(ctx, mconfig, out_fmt);
+ skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig);
+}
+
+static u16 skl_get_module_param_size(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig)
+{
+ u16 param_size;
+
+ switch (mconfig->m_type) {
+ case SKL_MODULE_TYPE_COPIER:
+ param_size = sizeof(struct skl_cpr_cfg);
+ param_size += mconfig->formats_config.caps_size;
+ return param_size;
+
+ case SKL_MODULE_TYPE_SRCINT:
+ return sizeof(struct skl_src_module_cfg);
+
+ case SKL_MODULE_TYPE_UPDWMIX:
+ return sizeof(struct skl_up_down_mixer_cfg);
+
+ default:
+ /*
+ * return only base cfg when no specific module type is
+ * specified
+ */
+ return sizeof(struct skl_base_cfg);
+ }
+
+ return 0;
+}
+
+/*
+ * DSP firmware supports various modules like copier, SRC, updown etc.
+ * These modules required various parameters to be calculated and sent for
+ * the module initialization to DSP. By default a generic module needs only
+ * base module format configuration
+ */
+
+static int skl_set_module_format(struct skl_sst *ctx,
+ struct skl_module_cfg *module_config,
+ u16 *module_config_size,
+ void **param_data)
+{
+ u16 param_size;
+
+ param_size = skl_get_module_param_size(ctx, module_config);
+
+ *param_data = kzalloc(param_size, GFP_KERNEL);
+ if (NULL == *param_data)
+ return -ENOMEM;
+
+ *module_config_size = param_size;
+
+ switch (module_config->m_type) {
+ case SKL_MODULE_TYPE_COPIER:
+ skl_set_copier_format(ctx, module_config, *param_data);
+ break;
+
+ case SKL_MODULE_TYPE_SRCINT:
+ skl_set_src_format(ctx, module_config, *param_data);
+ break;
+
+ case SKL_MODULE_TYPE_UPDWMIX:
+ skl_set_updown_mixer_format(ctx, module_config, *param_data);
+ break;
+
+ default:
+ skl_set_base_module_format(ctx, module_config, *param_data);
+ break;
+
+ }
+
+ dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n",
+ module_config->id.module_id, param_size);
+ print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4,
+ *param_data, param_size, false);
+ return 0;
+}
+
+static int skl_get_queue_index(struct skl_module_pin *mpin,
+ struct skl_module_inst_id id, int max)
+{
+ int i;
+
+ for (i = 0; i < max; i++) {
+ if (mpin[i].id.module_id == id.module_id &&
+ mpin[i].id.instance_id == id.instance_id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Allocates queue for each module.
+ * if dynamic, the pin_index is allocated 0 to max_pin.
+ * In static, the pin_index is fixed based on module_id and instance id
+ */
+static int skl_alloc_queue(struct skl_module_pin *mpin,
+ struct skl_module_inst_id id, int max)
+{
+ int i;
+
+ /*
+ * if pin in dynamic, find first free pin
+ * otherwise find match module and instance id pin as topology will
+ * ensure a unique pin is assigned to this so no need to
+ * allocate/free
+ */
+ for (i = 0; i < max; i++) {
+ if (mpin[i].is_dynamic) {
+ if (!mpin[i].in_use) {
+ mpin[i].in_use = true;
+ mpin[i].id.module_id = id.module_id;
+ mpin[i].id.instance_id = id.instance_id;
+ return i;
+ }
+ } else {
+ if (mpin[i].id.module_id == id.module_id &&
+ mpin[i].id.instance_id == id.instance_id)
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
+{
+ if (mpin[q_index].is_dynamic) {
+ mpin[q_index].in_use = false;
+ mpin[q_index].id.module_id = 0;
+ mpin[q_index].id.instance_id = 0;
+ }
+}
+
+/*
+ * A module needs to be instanataited in DSP. A mdoule is present in a
+ * collection of module referred as a PIPE.
+ * We first calculate the module format, based on module type and then
+ * invoke the DSP by sending IPC INIT_INSTANCE using ipc helper
+ */
+int skl_init_module(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig, char *param)
+{
+ u16 module_config_size = 0;
+ void *param_data = NULL;
+ int ret;
+ struct skl_ipc_init_instance_msg msg;
+
+ dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__,
+ mconfig->id.module_id, mconfig->id.instance_id);
+
+ if (mconfig->pipe->state != SKL_PIPE_CREATED) {
+ dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n",
+ mconfig->pipe->state, mconfig->pipe->ppl_id);
+ return -EIO;
+ }
+
+ ret = skl_set_module_format(ctx, mconfig,
+ &module_config_size, &param_data);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set module format ret=%d\n", ret);
+ return ret;
+ }
+
+ msg.module_id = mconfig->id.module_id;
+ msg.instance_id = mconfig->id.instance_id;
+ msg.ppl_instance_id = mconfig->pipe->ppl_id;
+ msg.param_data_size = module_config_size;
+ msg.core_id = mconfig->core_id;
+
+ ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to init instance ret=%d\n", ret);
+ kfree(param_data);
+ return ret;
+ }
+ mconfig->m_state = SKL_MODULE_INIT_DONE;
+
+ return ret;
+}
+
+static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg
+ *src_module, struct skl_module_cfg *dst_module)
+{
+ dev_dbg(ctx->dev, "%s: src module_id = %d src_instance=%d\n",
+ __func__, src_module->id.module_id, src_module->id.instance_id);
+ dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__,
+ dst_module->id.module_id, dst_module->id.instance_id);
+
+ dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n",
+ src_module->m_state, dst_module->m_state);
+}
+
+/*
+ * On module freeup, we need to unbind the module with modules
+ * it is already bind.
+ * Find the pin allocated and unbind then using bind_unbind IPC
+ */
+int skl_unbind_modules(struct skl_sst *ctx,
+ struct skl_module_cfg *src_mcfg,
+ struct skl_module_cfg *dst_mcfg)
+{
+ int ret;
+ struct skl_ipc_bind_unbind_msg msg;
+ struct skl_module_inst_id src_id = src_mcfg->id;
+ struct skl_module_inst_id dst_id = dst_mcfg->id;
+ int in_max = dst_mcfg->max_in_queue;
+ int out_max = src_mcfg->max_out_queue;
+ int src_index, dst_index;
+
+ skl_dump_bind_info(ctx, src_mcfg, dst_mcfg);
+
+ if (src_mcfg->m_state != SKL_MODULE_BIND_DONE)
+ return 0;
+
+ /*
+ * if intra module unbind, check if both modules are BIND,
+ * then send unbind
+ */
+ if ((src_mcfg->pipe->ppl_id != dst_mcfg->pipe->ppl_id) &&
+ dst_mcfg->m_state != SKL_MODULE_BIND_DONE)
+ return 0;
+ else if (src_mcfg->m_state < SKL_MODULE_INIT_DONE &&
+ dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
+ return 0;
+
+ /* get src queue index */
+ src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max);
+ if (src_index < 0)
+ return -EINVAL;
+
+ msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index;
+
+ /* get dst queue index */
+ dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max);
+ if (dst_index < 0)
+ return -EINVAL;
+
+ msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index;
+
+ msg.module_id = src_mcfg->id.module_id;
+ msg.instance_id = src_mcfg->id.instance_id;
+ msg.dst_module_id = dst_mcfg->id.module_id;
+ msg.dst_instance_id = dst_mcfg->id.instance_id;
+ msg.bind = false;
+
+ ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
+ if (!ret) {
+ src_mcfg->m_state = SKL_MODULE_UNINIT;
+ /* free queue only if unbind is success */
+ skl_free_queue(src_mcfg->m_out_pin, src_index);
+ skl_free_queue(dst_mcfg->m_in_pin, dst_index);
+ }
+
+ return ret;
+}
+
+/*
+ * Once a module is instantiated it need to be 'bind' with other modules in
+ * the pipeline. For binding we need to find the module pins which are bind
+ * together
+ * This function finds the pins and then sends bund_unbind IPC message to
+ * DSP using IPC helper
+ */
+int skl_bind_modules(struct skl_sst *ctx,
+ struct skl_module_cfg *src_mcfg,
+ struct skl_module_cfg *dst_mcfg)
+{
+ int ret;
+ struct skl_ipc_bind_unbind_msg msg;
+ struct skl_module_inst_id src_id = src_mcfg->id;
+ struct skl_module_inst_id dst_id = dst_mcfg->id;
+ int in_max = dst_mcfg->max_in_queue;
+ int out_max = src_mcfg->max_out_queue;
+ int src_index, dst_index;
+
+ skl_dump_bind_info(ctx, src_mcfg, dst_mcfg);
+
+ if (src_mcfg->m_state < SKL_MODULE_INIT_DONE &&
+ dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
+ return 0;
+
+ src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_id, out_max);
+ if (src_index < 0)
+ return -EINVAL;
+
+ msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index;
+ dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_id, in_max);
+ if (dst_index < 0) {
+ skl_free_queue(src_mcfg->m_out_pin, src_index);
+ return -EINVAL;
+ }
+
+ msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index;
+
+ dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n",
+ msg.src_queue, msg.dst_queue);
+
+ msg.module_id = src_mcfg->id.module_id;
+ msg.instance_id = src_mcfg->id.instance_id;
+ msg.dst_module_id = dst_mcfg->id.module_id;
+ msg.dst_instance_id = dst_mcfg->id.instance_id;
+ msg.bind = true;
+
+ ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
+
+ if (!ret) {
+ src_mcfg->m_state = SKL_MODULE_BIND_DONE;
+ } else {
+ /* error case , if IPC fails, clear the queue index */
+ skl_free_queue(src_mcfg->m_out_pin, src_index);
+ skl_free_queue(dst_mcfg->m_in_pin, dst_index);
+ }
+
+ return ret;
+}
+
+static int skl_set_pipe_state(struct skl_sst *ctx, struct skl_pipe *pipe,
+ enum skl_ipc_pipeline_state state)
+{
+ dev_dbg(ctx->dev, "%s: pipe_satate = %d\n", __func__, state);
+
+ return skl_ipc_set_pipeline_state(&ctx->ipc, pipe->ppl_id, state);
+}
+
+/*
+ * A pipeline is a collection of modules. Before a module in instantiated a
+ * pipeline needs to be created for it.
+ * This function creates pipeline, by sending create pipeline IPC messages
+ * to FW
+ */
+int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+ int ret;
+
+ dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id);
+
+ ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages,
+ pipe->pipe_priority, pipe->ppl_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to create pipeline\n");
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_CREATED;
+
+ return 0;
+}
+
+/*
+ * A pipeline needs to be deleted on cleanup. If a pipeline is running, then
+ * pause the pipeline first and then delete it
+ * The pipe delete is done by sending delete pipeline IPC. DSP will stop the
+ * DMA engines and releases resources
+ */
+int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+ int ret;
+
+ dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
+
+ /* If pipe is not started, do not try to stop the pipe in FW. */
+ if (pipe->state > SKL_PIPE_STARTED) {
+ ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to stop pipeline\n");
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_PAUSED;
+ } else {
+ /* If pipe was not created in FW, do not try to delete it */
+ if (pipe->state < SKL_PIPE_CREATED)
+ return 0;
+
+ ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
+ if (ret < 0)
+ dev_err(ctx->dev, "Failed to delete pipeline\n");
+ }
+
+ return ret;
+}
+
+/*
+ * A pipeline is also a scheduling entity in DSP which can be run, stopped
+ * For processing data the pipe need to be run by sending IPC set pipe state
+ * to DSP
+ */
+int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+ int ret;
+
+ dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
+
+ /* If pipe was not created in FW, do not try to pause or delete */
+ if (pipe->state < SKL_PIPE_CREATED)
+ return 0;
+
+ /* Pipe has to be paused before it is started */
+ ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to pause pipe\n");
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_PAUSED;
+
+ ret = skl_set_pipe_state(ctx, pipe, PPL_RUNNING);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to start pipe\n");
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_STARTED;
+
+ return 0;
+}
+
+/*
+ * Stop the pipeline by sending set pipe state IPC
+ * DSP doesnt implement stop so we always send pause message
+ */
+int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+ int ret;
+
+ dev_dbg(ctx->dev, "In %s pipe=%d\n", __func__, pipe->ppl_id);
+
+ /* If pipe was not created in FW, do not try to pause or delete */
+ if (pipe->state < SKL_PIPE_PAUSED)
+ return 0;
+
+ ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "Failed to stop pipe\n");
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_CREATED;
+
+ return 0;
+}
diff --git a/kernel/sound/soc/intel/skylake/skl-nhlt.c b/kernel/sound/soc/intel/skylake/skl-nhlt.c
new file mode 100644
index 000000000..b0c7bd113
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-nhlt.c
@@ -0,0 +1,140 @@
+/*
+ * skl-nhlt.c - Intel SKL Platform NHLT parsing
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+#include "skl.h"
+
+/* Unique identification for getting NHLT blobs */
+static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
+ 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53};
+
+#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
+
+void *skl_nhlt_init(struct device *dev)
+{
+ acpi_handle handle;
+ union acpi_object *obj;
+ struct nhlt_resource_desc *nhlt_ptr = NULL;
+
+ if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
+ dev_err(dev, "Requested NHLT device not found\n");
+ return NULL;
+ }
+
+ obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL);
+ if (obj && obj->type == ACPI_TYPE_BUFFER) {
+ nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
+
+ return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+ MEMREMAP_WB);
+ }
+
+ dev_err(dev, "device specific method to extract NHLT blob failed\n");
+ return NULL;
+}
+
+void skl_nhlt_free(void *addr)
+{
+ memunmap(addr);
+}
+
+static struct nhlt_specific_cfg *skl_get_specific_cfg(
+ struct device *dev, struct nhlt_fmt *fmt,
+ u8 no_ch, u32 rate, u16 bps)
+{
+ struct nhlt_specific_cfg *sp_config;
+ struct wav_fmt *wfmt;
+ struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config;
+ int i;
+
+ dev_dbg(dev, "Format count =%d\n", fmt->fmt_count);
+
+ for (i = 0; i < fmt->fmt_count; i++) {
+ wfmt = &fmt_config->fmt_ext.fmt;
+ dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels,
+ wfmt->bits_per_sample, wfmt->samples_per_sec);
+ if (wfmt->channels == no_ch && wfmt->samples_per_sec == rate &&
+ wfmt->bits_per_sample == bps) {
+ sp_config = &fmt_config->config;
+
+ return sp_config;
+ }
+
+ fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps +
+ fmt_config->config.size);
+ }
+
+ return NULL;
+}
+
+static void dump_config(struct device *dev, u32 instance_id, u8 linktype,
+ u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps)
+{
+ dev_dbg(dev, "Input configuration\n");
+ dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate);
+ dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype);
+ dev_dbg(dev, "bits_per_sample=%d\n", bps);
+}
+
+static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
+ u32 instance_id, u8 link_type, u8 dirn)
+{
+ dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d\n",
+ epnt->virtual_bus_id, epnt->linktype, epnt->direction);
+
+ if ((epnt->virtual_bus_id == instance_id) &&
+ (epnt->linktype == link_type) &&
+ (epnt->direction == dirn))
+ return true;
+ else
+ return false;
+}
+
+struct nhlt_specific_cfg
+*skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type,
+ u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn)
+{
+ struct nhlt_fmt *fmt;
+ struct nhlt_endpoint *epnt;
+ struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
+ struct device *dev = bus->dev;
+ struct nhlt_specific_cfg *sp_config;
+ struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+ u16 bps = num_ch * s_fmt;
+ u8 j;
+
+ dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps);
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count);
+
+ for (j = 0; j < nhlt->endpoint_count; j++) {
+ if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) {
+ fmt = (struct nhlt_fmt *)(epnt->config.caps +
+ epnt->config.size);
+ sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps);
+ if (sp_config)
+ return sp_config;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return NULL;
+}
diff --git a/kernel/sound/soc/intel/skylake/skl-nhlt.h b/kernel/sound/soc/intel/skylake/skl-nhlt.h
new file mode 100644
index 000000000..3769f9fef
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-nhlt.h
@@ -0,0 +1,106 @@
+/*
+ * skl-nhlt.h - Intel HDA Platform NHLT header
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+#ifndef __SKL_NHLT_H__
+#define __SKL_NHLT_H__
+
+#include <linux/acpi.h>
+
+struct wav_fmt {
+ u16 fmt_tag;
+ u16 channels;
+ u32 samples_per_sec;
+ u32 avg_bytes_per_sec;
+ u16 block_align;
+ u16 bits_per_sample;
+ u16 cb_size;
+} __packed;
+
+struct wav_fmt_ext {
+ struct wav_fmt fmt;
+ union samples {
+ u16 valid_bits_per_sample;
+ u16 samples_per_block;
+ u16 reserved;
+ } sample;
+ u32 channel_mask;
+ u8 sub_fmt[16];
+} __packed;
+
+enum nhlt_link_type {
+ NHLT_LINK_HDA = 0,
+ NHLT_LINK_DSP = 1,
+ NHLT_LINK_DMIC = 2,
+ NHLT_LINK_SSP = 3,
+ NHLT_LINK_INVALID
+};
+
+enum nhlt_device_type {
+ NHLT_DEVICE_BT = 0,
+ NHLT_DEVICE_DMIC = 1,
+ NHLT_DEVICE_I2S = 4,
+ NHLT_DEVICE_INVALID
+};
+
+struct nhlt_specific_cfg {
+ u32 size;
+ u8 caps[0];
+} __packed;
+
+struct nhlt_fmt_cfg {
+ struct wav_fmt_ext fmt_ext;
+ struct nhlt_specific_cfg config;
+} __packed;
+
+struct nhlt_fmt {
+ u8 fmt_count;
+ struct nhlt_fmt_cfg fmt_config[0];
+} __packed;
+
+struct nhlt_endpoint {
+ u32 length;
+ u8 linktype;
+ u8 instance_id;
+ u16 vendor_id;
+ u16 device_id;
+ u16 revision_id;
+ u32 subsystem_id;
+ u8 device_type;
+ u8 direction;
+ u8 virtual_bus_id;
+ struct nhlt_specific_cfg config;
+} __packed;
+
+struct nhlt_acpi_table {
+ struct acpi_table_header header;
+ u8 endpoint_count;
+ struct nhlt_endpoint desc[0];
+} __packed;
+
+struct nhlt_resource_desc {
+ u32 extra;
+ u16 flags;
+ u64 addr_spc_gra;
+ u64 min_addr;
+ u64 max_addr;
+ u64 addr_trans_offset;
+ u64 length;
+} __packed;
+
+#endif
diff --git a/kernel/sound/soc/intel/skylake/skl-pcm.c b/kernel/sound/soc/intel/skylake/skl-pcm.c
new file mode 100644
index 000000000..a2f94ce16
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-pcm.c
@@ -0,0 +1,968 @@
+/*
+ * skl-pcm.c -ASoC HDA Platform driver file implementing PCM functionality
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "skl.h"
+#include "skl-topology.h"
+
+#define HDA_MONO 1
+#define HDA_STEREO 2
+
+static struct snd_pcm_hardware azx_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
+ SNDRV_PCM_INFO_HAS_LINK_ATIME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+static inline
+struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+
+static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct hdac_stream *hstream = hdac_stream(stream);
+ struct hdac_bus *bus = hstream->bus;
+
+ return hbus_to_ebus(bus);
+}
+
+static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus,
+ struct snd_pcm_substream *substream,
+ size_t size)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+
+ hdac_stream(stream)->bufsize = 0;
+ hdac_stream(stream)->period_bytes = 0;
+ hdac_stream(stream)->format_val = 0;
+
+ return snd_pcm_lib_malloc_pages(substream, size);
+}
+
+static int skl_substream_free_pages(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
+ struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ 20, 178000000);
+}
+
+static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus)
+{
+ if (ebus->ppcap)
+ return HDAC_EXT_STREAM_TYPE_HOST;
+ else
+ return HDAC_EXT_STREAM_TYPE_COUPLED;
+}
+
+static int skl_pcm_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct skl_dma_params *dma_params;
+ int ret;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ ret = pm_runtime_get_sync(dai->dev);
+ if (ret < 0)
+ return ret;
+
+ stream = snd_hdac_ext_stream_assign(ebus, substream,
+ skl_get_host_stream_type(ebus));
+ if (stream == NULL)
+ return -EBUSY;
+
+ skl_set_pcm_constrains(ebus, runtime);
+
+ /*
+ * disable WALLCLOCK timestamps for capture streams
+ * until we figure out how to handle digital inputs
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
+ }
+
+ runtime->private_data = stream;
+
+ dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL);
+ if (!dma_params)
+ return -ENOMEM;
+
+ dma_params->stream_tag = hdac_stream(stream)->stream_tag;
+ snd_soc_dai_set_dma_data(dai, substream, dma_params);
+
+ dev_dbg(dai->dev, "stream tag set in dma params=%d\n",
+ dma_params->stream_tag);
+ snd_pcm_set_sync(substream);
+
+ return 0;
+}
+
+static int skl_get_format(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct skl_dma_params *dma_params;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ int format_val = 0;
+
+ if (ebus->ppcap) {
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ format_val = snd_hdac_calc_stream_format(runtime->rate,
+ runtime->channels,
+ runtime->format,
+ 32, 0);
+ } else {
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
+ if (dma_params)
+ format_val = dma_params->format;
+ }
+
+ return format_val;
+}
+
+static int skl_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ unsigned int format_val;
+ int err;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ if (hdac_stream(stream)->prepared) {
+ dev_dbg(dai->dev, "already stream is prepared - returning\n");
+ return 0;
+ }
+
+ format_val = skl_get_format(substream, dai);
+ dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
+ hdac_stream(stream)->stream_tag, format_val);
+ snd_hdac_stream_reset(hdac_stream(stream));
+
+ err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
+ if (err < 0)
+ return err;
+
+ err = snd_hdac_stream_setup(hdac_stream(stream));
+ if (err < 0)
+ return err;
+
+ hdac_stream(stream)->prepared = 1;
+
+ return err;
+}
+
+static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct skl_pipe_params p_params = {0};
+ struct skl_module_cfg *m_cfg;
+ int ret, dma_id;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ ret = skl_substream_alloc_pages(ebus, substream,
+ params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n",
+ runtime->rate, runtime->channels, runtime->format);
+
+ dma_id = hdac_stream(stream)->stream_tag - 1;
+ dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.host_dma_id = dma_id;
+ p_params.stream = substream->stream;
+
+ m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
+ if (m_cfg)
+ skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params);
+
+ return 0;
+}
+
+static void skl_pcm_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct skl_dma_params *dma_params = NULL;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(ebus));
+
+ dma_params = snd_soc_dai_get_dma_data(dai, substream);
+ /*
+ * now we should set this to NULL as we are freeing by the
+ * dma_params
+ */
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+
+ pm_runtime_mark_last_busy(dai->dev);
+ pm_runtime_put_autosuspend(dai->dev);
+ kfree(dma_params);
+}
+
+static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ snd_hdac_stream_cleanup(hdac_stream(stream));
+ hdac_stream(stream)->prepared = 0;
+
+ return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
+}
+
+static int skl_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct skl_pipe_params p_params = {0};
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.stream = substream->stream;
+ skl_tplg_be_update_params(dai, &p_params);
+
+ return 0;
+}
+
+static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_module_cfg *mconfig;
+
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+ if (!mconfig)
+ return -EIO;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ return skl_run_pipe(ctx, mconfig->pipe);
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ return skl_stop_pipe(ctx, mconfig->pipe);
+
+ default:
+ return 0;
+ }
+}
+
+static int skl_link_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *link_dev;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct skl_dma_params *dma_params;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct skl_pipe_params p_params = {0};
+
+ link_dev = snd_hdac_ext_stream_assign(ebus, substream,
+ HDAC_EXT_STREAM_TYPE_LINK);
+ if (!link_dev)
+ return -EBUSY;
+
+ snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
+
+ /* set the stream tag in the codec dai dma params */
+ dma_params = (struct skl_dma_params *)
+ snd_soc_dai_get_dma_data(codec_dai, substream);
+ if (dma_params)
+ dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.stream = substream->stream;
+ p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+ skl_tplg_be_update_params(dai, &p_params);
+
+ return 0;
+}
+
+static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *link_dev =
+ snd_soc_dai_get_dma_data(dai, substream);
+ unsigned int format_val = 0;
+ struct skl_dma_params *dma_params;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct hdac_ext_link *link;
+
+ if (link_dev->link_prepared) {
+ dev_dbg(dai->dev, "already stream is prepared - returning\n");
+ return 0;
+ }
+
+ dma_params = (struct skl_dma_params *)
+ snd_soc_dai_get_dma_data(codec_dai, substream);
+ if (dma_params)
+ format_val = dma_params->format;
+ dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n",
+ hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name);
+
+ snd_hdac_ext_link_stream_reset(link_dev);
+
+ snd_hdac_ext_link_stream_setup(link_dev, format_val);
+
+ link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
+ if (!link)
+ return -EINVAL;
+
+ snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
+ link_dev->link_prepared = 1;
+
+ return 0;
+}
+
+static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *link_dev =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ snd_hdac_ext_link_stream_start(link_dev);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_hdac_ext_link_stream_clear(link_dev);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int skl_link_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_stream *link_dev =
+ snd_soc_dai_get_dma_data(dai, substream);
+ struct hdac_ext_link *link;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ link_dev->link_prepared = 0;
+
+ link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
+ if (!link)
+ return -EINVAL;
+
+ snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag);
+ snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
+ return 0;
+}
+
+static int skl_be_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return pm_runtime_get_sync(dai->dev);
+}
+
+static void skl_be_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pm_runtime_mark_last_busy(dai->dev);
+ pm_runtime_put_autosuspend(dai->dev);
+}
+
+static struct snd_soc_dai_ops skl_pcm_dai_ops = {
+ .startup = skl_pcm_open,
+ .shutdown = skl_pcm_close,
+ .prepare = skl_pcm_prepare,
+ .hw_params = skl_pcm_hw_params,
+ .hw_free = skl_pcm_hw_free,
+ .trigger = skl_pcm_trigger,
+};
+
+static struct snd_soc_dai_ops skl_dmic_dai_ops = {
+ .startup = skl_be_startup,
+ .hw_params = skl_be_hw_params,
+ .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
+ .startup = skl_be_startup,
+ .hw_params = skl_be_hw_params,
+ .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_link_dai_ops = {
+ .startup = skl_be_startup,
+ .prepare = skl_link_pcm_prepare,
+ .hw_params = skl_link_hw_params,
+ .hw_free = skl_link_hw_free,
+ .trigger = skl_link_pcm_trigger,
+ .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_driver skl_platform_dai[] = {
+{
+ .name = "System Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "System Playback",
+ .channels_min = HDA_MONO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "System Capture",
+ .channels_min = HDA_MONO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "Reference Pin",
+ .ops = &skl_pcm_dai_ops,
+ .capture = {
+ .stream_name = "Reference Capture",
+ .channels_min = HDA_MONO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "Deepbuffer Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "Deepbuffer Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "LowLatency Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "Low Latency Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+/* BE CPU Dais */
+{
+ .name = "SSP0 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp0 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp0 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "iDisp Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "iDisp Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "DMIC01 Pin",
+ .ops = &skl_dmic_dai_ops,
+ .capture = {
+ .stream_name = "DMIC01 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "HD-Codec Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "HD-Codec Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "HD-Codec Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static int skl_platform_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__,
+ dai_link->cpu_dai_name);
+
+ runtime = substream->runtime;
+ snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw);
+
+ return 0;
+}
+
+static int skl_coupled_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_ext_stream *stream;
+ struct snd_pcm_substream *s;
+ bool start;
+ int sbits = 0;
+ unsigned long cookie;
+ struct hdac_stream *hstr;
+
+ stream = get_hdac_ext_stream(substream);
+ hstr = hdac_stream(stream);
+
+ dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd);
+
+ if (!hstr->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = true;
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = false;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ stream = get_hdac_ext_stream(s);
+ sbits |= 1 << hdac_stream(stream)->index;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+
+ /* first, set SYNC bits of corresponding streams */
+ snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC);
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ stream = get_hdac_ext_stream(s);
+ if (start)
+ snd_hdac_stream_start(hdac_stream(stream), true);
+ else
+ snd_hdac_stream_stop(hdac_stream(stream));
+ }
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+ snd_hdac_stream_sync(hstr, start, sbits);
+
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+
+ /* reset SYNC bits */
+ snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC);
+ if (start)
+ snd_hdac_stream_timecounter_init(hstr, sbits);
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+ return 0;
+}
+
+static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct hdac_ext_stream *stream;
+ int start;
+ unsigned long cookie;
+ struct hdac_stream *hstr;
+
+ dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name);
+
+ stream = get_hdac_ext_stream(substream);
+ hstr = hdac_stream(stream);
+
+ if (!hstr->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+
+ if (start)
+ snd_hdac_stream_start(hdac_stream(stream), true);
+ else
+ snd_hdac_stream_stop(hdac_stream(stream));
+
+ if (start)
+ snd_hdac_stream_timecounter_init(hstr, 0);
+
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+ return 0;
+}
+static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+
+ if (ebus->ppcap)
+ return skl_decoupled_trigger(substream, cmd);
+ else
+ return skl_coupled_trigger(substream, cmd);
+}
+
+/* calculate runtime delay from LPIB */
+static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
+ struct hdac_ext_stream *sstream,
+ unsigned int pos)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_stream *hstream = hdac_stream(sstream);
+ struct snd_pcm_substream *substream = hstream->substream;
+ int stream = substream->stream;
+ unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream);
+ int delay;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = pos - lpib_pos;
+ else
+ delay = lpib_pos - pos;
+
+ if (delay < 0) {
+ if (delay >= hstream->delay_negative_threshold)
+ delay = 0;
+ else
+ delay += hstream->bufsize;
+ }
+
+ if (delay >= hstream->period_bytes) {
+ dev_info(bus->dev,
+ "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
+ delay, hstream->period_bytes);
+ delay = 0;
+ }
+
+ return bytes_to_frames(substream->runtime, delay);
+}
+
+static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
+ int codec_delay)
+{
+ struct hdac_stream *hstr = hdac_stream(hstream);
+ struct snd_pcm_substream *substream = hstr->substream;
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ unsigned int pos;
+ int delay;
+
+ /* use the position buffer as default */
+ pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
+
+ if (pos >= hdac_stream(hstream)->bufsize)
+ pos = 0;
+
+ if (substream->runtime) {
+ delay = skl_get_delay_from_lpib(ebus, hstream, pos)
+ + codec_delay;
+ substream->runtime->delay += delay;
+ }
+
+ return pos;
+}
+
+static snd_pcm_uframes_t skl_platform_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
+
+ return bytes_to_frames(substream->runtime,
+ skl_get_position(hstream, 0));
+}
+
+static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
+ u64 nsec)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ u64 codec_frames, codec_nsecs;
+
+ if (!codec_dai->driver->ops->delay)
+ return nsec;
+
+ codec_frames = codec_dai->driver->ops->delay(substream, codec_dai);
+ codec_nsecs = div_u64(codec_frames * 1000000000LL,
+ substream->runtime->rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return nsec + codec_nsecs;
+
+ return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
+static int skl_get_time_info(struct snd_pcm_substream *substream,
+ struct timespec *system_ts, struct timespec *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+ struct hdac_ext_stream *sstream = get_hdac_ext_stream(substream);
+ struct hdac_stream *hstr = hdac_stream(sstream);
+ u64 nsec;
+
+ if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
+ (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
+
+ snd_pcm_gettime(substream->runtime, system_ts);
+
+ nsec = timecounter_read(&hstr->tc);
+ nsec = div_u64(nsec, 3); /* can be optimized */
+ if (audio_tstamp_config->report_delay)
+ nsec = skl_adjust_codec_delay(substream, nsec);
+
+ *audio_ts = ns_to_timespec(nsec);
+
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+ audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */
+ audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */
+
+ } else {
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+ }
+
+ return 0;
+}
+
+static struct snd_pcm_ops skl_platform_ops = {
+ .open = skl_platform_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .trigger = skl_platform_pcm_trigger,
+ .pointer = skl_platform_pcm_pointer,
+ .get_time_info = skl_get_time_info,
+ .mmap = snd_pcm_lib_default_mmap,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static void skl_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct snd_pcm *pcm = rtd->pcm;
+ unsigned int size;
+ int retval = 0;
+ struct skl *skl = ebus_to_skl(ebus);
+
+ if (dai->driver->playback.channels_min ||
+ dai->driver->capture.channels_min) {
+ /* buffer pre-allocation */
+ size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
+ if (size > MAX_PREALLOC_SIZE)
+ size = MAX_PREALLOC_SIZE;
+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(skl->pci),
+ size, MAX_PREALLOC_SIZE);
+ if (retval) {
+ dev_err(dai->dev, "dma buffer allocationf fail\n");
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+static int skl_platform_soc_probe(struct snd_soc_platform *platform)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+
+ if (ebus->ppcap)
+ return skl_tplg_init(platform, ebus);
+
+ return 0;
+}
+static struct snd_soc_platform_driver skl_platform_drv = {
+ .probe = skl_platform_soc_probe,
+ .ops = &skl_platform_ops,
+ .pcm_new = skl_pcm_new,
+ .pcm_free = skl_pcm_free,
+};
+
+static const struct snd_soc_component_driver skl_component = {
+ .name = "pcm",
+};
+
+int skl_platform_register(struct device *dev)
+{
+ int ret;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ INIT_LIST_HEAD(&skl->ppl_list);
+ INIT_LIST_HEAD(&skl->dapm_path_list);
+
+ ret = snd_soc_register_platform(dev, &skl_platform_drv);
+ if (ret) {
+ dev_err(dev, "soc platform registration failed %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_register_component(dev, &skl_component,
+ skl_platform_dai,
+ ARRAY_SIZE(skl_platform_dai));
+ if (ret) {
+ dev_err(dev, "soc component registration failed %d\n", ret);
+ snd_soc_unregister_platform(dev);
+ }
+
+ return ret;
+
+}
+
+int skl_platform_unregister(struct device *dev)
+{
+ snd_soc_unregister_component(dev);
+ snd_soc_unregister_platform(dev);
+ return 0;
+}
diff --git a/kernel/sound/soc/intel/skylake/skl-sst-cldma.c b/kernel/sound/soc/intel/skylake/skl-sst-cldma.c
new file mode 100644
index 000000000..44748ba98
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst-cldma.c
@@ -0,0 +1,327 @@
+/*
+ * skl-sst-cldma.c - Code Loader DMA handler
+ *
+ * Copyright (C) 2015, Intel Corporation.
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/kthread.h>
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+
+static void skl_cldma_int_enable(struct sst_dsp *ctx)
+{
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC,
+ SKL_ADSPIC_CL_DMA, SKL_ADSPIC_CL_DMA);
+}
+
+void skl_cldma_int_disable(struct sst_dsp *ctx)
+{
+ sst_dsp_shim_update_bits_unlocked(ctx,
+ SKL_ADSP_REG_ADSPIC, SKL_ADSPIC_CL_DMA, 0);
+}
+
+/* Code loader helper APIs */
+static void skl_cldma_setup_bdle(struct sst_dsp *ctx,
+ struct snd_dma_buffer *dmab_data,
+ u32 **bdlp, int size, int with_ioc)
+{
+ u32 *bdl = *bdlp;
+
+ ctx->cl_dev.frags = 0;
+ while (size > 0) {
+ phys_addr_t addr = virt_to_phys(dmab_data->area +
+ (ctx->cl_dev.frags * ctx->cl_dev.bufsize));
+
+ bdl[0] = cpu_to_le32(lower_32_bits(addr));
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+
+ bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize);
+
+ size -= ctx->cl_dev.bufsize;
+ bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+
+ bdl += 4;
+ ctx->cl_dev.frags++;
+ }
+}
+
+/*
+ * Setup controller
+ * Configure the registers to update the dma buffer address and
+ * enable interrupts.
+ * Note: Using the channel 1 for transfer
+ */
+static void skl_cldma_setup_controller(struct sst_dsp *ctx,
+ struct snd_dma_buffer *dmab_bdl, unsigned int max_size,
+ u32 count)
+{
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL,
+ CL_SD_BDLPLBA(dmab_bdl->addr));
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU,
+ CL_SD_BDLPUBA(dmab_bdl->addr));
+
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, max_size);
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, count - 1);
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(1));
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(1));
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(1));
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(FW_CL_STREAM_NUMBER));
+}
+
+static void skl_cldma_setup_spb(struct sst_dsp *ctx,
+ unsigned int size, bool enable)
+{
+ if (enable)
+ sst_dsp_shim_update_bits_unlocked(ctx,
+ SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL,
+ CL_SPBFIFO_SPBFCCTL_SPIBE_MASK,
+ CL_SPBFIFO_SPBFCCTL_SPIBE(1));
+
+ sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, size);
+}
+
+static void skl_cldma_cleanup_spb(struct sst_dsp *ctx)
+{
+ sst_dsp_shim_update_bits_unlocked(ctx,
+ SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL,
+ CL_SPBFIFO_SPBFCCTL_SPIBE_MASK,
+ CL_SPBFIFO_SPBFCCTL_SPIBE(0));
+
+ sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, 0);
+}
+
+static void skl_cldma_trigger(struct sst_dsp *ctx, bool enable)
+{
+ if (enable)
+ sst_dsp_shim_update_bits_unlocked(ctx,
+ SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(1));
+ else
+ sst_dsp_shim_update_bits_unlocked(ctx,
+ SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(0));
+}
+
+static void skl_cldma_cleanup(struct sst_dsp *ctx)
+{
+ skl_cldma_cleanup_spb(ctx);
+
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0));
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0));
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0));
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
+ CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0));
+
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0));
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0);
+
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0);
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0);
+}
+
+static int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
+{
+ int ret = 0;
+
+ if (!wait_event_timeout(ctx->cl_dev.wait_queue,
+ ctx->cl_dev.wait_condition,
+ msecs_to_jiffies(SKL_WAIT_TIMEOUT))) {
+ dev_err(ctx->dev, "%s: Wait timeout\n", __func__);
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ dev_dbg(ctx->dev, "%s: Event wake\n", __func__);
+ if (ctx->cl_dev.wake_status != SKL_CL_DMA_BUF_COMPLETE) {
+ dev_err(ctx->dev, "%s: DMA Error\n", __func__);
+ ret = -EIO;
+ }
+
+cleanup:
+ ctx->cl_dev.wake_status = SKL_CL_DMA_STATUS_NONE;
+ return ret;
+}
+
+static void skl_cldma_stop(struct sst_dsp *ctx)
+{
+ ctx->cl_dev.ops.cl_trigger(ctx, false);
+}
+
+static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
+ const void *curr_pos, bool intr_enable, bool trigger)
+{
+ dev_dbg(ctx->dev, "Size: %x, intr_enable: %d\n", size, intr_enable);
+ dev_dbg(ctx->dev, "buf_pos_index:%d, trigger:%d\n",
+ ctx->cl_dev.dma_buffer_offset, trigger);
+ dev_dbg(ctx->dev, "spib position: %d\n", ctx->cl_dev.curr_spib_pos);
+
+ memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
+ curr_pos, size);
+
+ if (ctx->cl_dev.curr_spib_pos == ctx->cl_dev.bufsize)
+ ctx->cl_dev.dma_buffer_offset = 0;
+ else
+ ctx->cl_dev.dma_buffer_offset = ctx->cl_dev.curr_spib_pos;
+
+ ctx->cl_dev.wait_condition = false;
+
+ if (intr_enable)
+ skl_cldma_int_enable(ctx);
+
+ ctx->cl_dev.ops.cl_setup_spb(ctx, ctx->cl_dev.curr_spib_pos, trigger);
+ if (trigger)
+ ctx->cl_dev.ops.cl_trigger(ctx, true);
+}
+
+/*
+ * The CL dma doesn't have any way to update the transfer status until a BDL
+ * buffer is fully transferred
+ *
+ * So Copying is divided in two parts.
+ * 1. Interrupt on buffer done where the size to be transferred is more than
+ * ring buffer size.
+ * 2. Polling on fw register to identify if data left to transferred doesn't
+ * fill the ring buffer. Caller takes care of polling the required status
+ * register to identify the transfer status.
+ */
+static int
+skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
+{
+ int ret = 0;
+ bool start = true;
+ unsigned int excess_bytes;
+ u32 size;
+ unsigned int bytes_left = total_size;
+ const void *curr_pos = bin;
+
+ if (total_size <= 0)
+ return -EINVAL;
+
+ dev_dbg(ctx->dev, "%s: Total binary size: %u\n", __func__, bytes_left);
+
+ while (bytes_left) {
+ if (bytes_left > ctx->cl_dev.bufsize) {
+
+ /*
+ * dma transfers only till the write pointer as
+ * updated in spib
+ */
+ if (ctx->cl_dev.curr_spib_pos == 0)
+ ctx->cl_dev.curr_spib_pos = ctx->cl_dev.bufsize;
+
+ size = ctx->cl_dev.bufsize;
+ skl_cldma_fill_buffer(ctx, size, curr_pos, true, start);
+
+ start = false;
+ ret = skl_cldma_wait_interruptible(ctx);
+ if (ret < 0) {
+ skl_cldma_stop(ctx);
+ return ret;
+ }
+
+ } else {
+ skl_cldma_int_disable(ctx);
+
+ if ((ctx->cl_dev.curr_spib_pos + bytes_left)
+ <= ctx->cl_dev.bufsize) {
+ ctx->cl_dev.curr_spib_pos += bytes_left;
+ } else {
+ excess_bytes = bytes_left -
+ (ctx->cl_dev.bufsize -
+ ctx->cl_dev.curr_spib_pos);
+ ctx->cl_dev.curr_spib_pos = excess_bytes;
+ }
+
+ size = bytes_left;
+ skl_cldma_fill_buffer(ctx, size,
+ curr_pos, false, start);
+ }
+ bytes_left -= size;
+ curr_pos = curr_pos + size;
+ }
+
+ return ret;
+}
+
+void skl_cldma_process_intr(struct sst_dsp *ctx)
+{
+ u8 cl_dma_intr_status;
+
+ cl_dma_intr_status =
+ sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_CL_SD_STS);
+
+ if (!(cl_dma_intr_status & SKL_CL_DMA_SD_INT_COMPLETE))
+ ctx->cl_dev.wake_status = SKL_CL_DMA_ERR;
+ else
+ ctx->cl_dev.wake_status = SKL_CL_DMA_BUF_COMPLETE;
+
+ ctx->cl_dev.wait_condition = true;
+ wake_up(&ctx->cl_dev.wait_queue);
+}
+
+int skl_cldma_prepare(struct sst_dsp *ctx)
+{
+ int ret;
+ u32 *bdl;
+
+ ctx->cl_dev.bufsize = SKL_MAX_BUFFER_SIZE;
+
+ /* Allocate cl ops */
+ ctx->cl_dev.ops.cl_setup_bdle = skl_cldma_setup_bdle;
+ ctx->cl_dev.ops.cl_setup_controller = skl_cldma_setup_controller;
+ ctx->cl_dev.ops.cl_setup_spb = skl_cldma_setup_spb;
+ ctx->cl_dev.ops.cl_cleanup_spb = skl_cldma_cleanup_spb;
+ ctx->cl_dev.ops.cl_trigger = skl_cldma_trigger;
+ ctx->cl_dev.ops.cl_cleanup_controller = skl_cldma_cleanup;
+ ctx->cl_dev.ops.cl_copy_to_dmabuf = skl_cldma_copy_to_buf;
+ ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop;
+
+ /* Allocate buffer*/
+ ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
+ &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret);
+ return ret;
+ }
+ /* Setup Code loader BDL */
+ ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
+ &ctx->cl_dev.dmab_bdl, PAGE_SIZE);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret);
+ ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
+ return ret;
+ }
+ bdl = (u32 *)ctx->cl_dev.dmab_bdl.area;
+
+ /* Allocate BDLs */
+ ctx->cl_dev.ops.cl_setup_bdle(ctx, &ctx->cl_dev.dmab_data,
+ &bdl, ctx->cl_dev.bufsize, 1);
+ ctx->cl_dev.ops.cl_setup_controller(ctx, &ctx->cl_dev.dmab_bdl,
+ ctx->cl_dev.bufsize, ctx->cl_dev.frags);
+
+ ctx->cl_dev.curr_spib_pos = 0;
+ ctx->cl_dev.dma_buffer_offset = 0;
+ init_waitqueue_head(&ctx->cl_dev.wait_queue);
+
+ return ret;
+}
diff --git a/kernel/sound/soc/intel/skylake/skl-sst-cldma.h b/kernel/sound/soc/intel/skylake/skl-sst-cldma.h
new file mode 100644
index 000000000..99e4c86b6
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst-cldma.h
@@ -0,0 +1,251 @@
+/*
+ * Intel Code Loader DMA support
+ *
+ * Copyright (C) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef SKL_SST_CLDMA_H_
+#define SKL_SST_CLDMA_H_
+
+#define FW_CL_STREAM_NUMBER 0x1
+
+#define DMA_ADDRESS_128_BITS_ALIGNMENT 7
+#define BDL_ALIGN(x) (x >> DMA_ADDRESS_128_BITS_ALIGNMENT)
+
+#define SKL_ADSPIC_CL_DMA 0x2
+#define SKL_ADSPIS_CL_DMA 0x2
+#define SKL_CL_DMA_SD_INT_DESC_ERR 0x10 /* Descriptor error interrupt */
+#define SKL_CL_DMA_SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
+#define SKL_CL_DMA_SD_INT_COMPLETE 0x04 /* Buffer completion interrupt */
+
+/* Intel HD Audio Code Loader DMA Registers */
+
+#define HDA_ADSP_LOADER_BASE 0x80
+
+/* Stream Registers */
+#define SKL_ADSP_REG_CL_SD_CTL (HDA_ADSP_LOADER_BASE + 0x00)
+#define SKL_ADSP_REG_CL_SD_STS (HDA_ADSP_LOADER_BASE + 0x03)
+#define SKL_ADSP_REG_CL_SD_LPIB (HDA_ADSP_LOADER_BASE + 0x04)
+#define SKL_ADSP_REG_CL_SD_CBL (HDA_ADSP_LOADER_BASE + 0x08)
+#define SKL_ADSP_REG_CL_SD_LVI (HDA_ADSP_LOADER_BASE + 0x0c)
+#define SKL_ADSP_REG_CL_SD_FIFOW (HDA_ADSP_LOADER_BASE + 0x0e)
+#define SKL_ADSP_REG_CL_SD_FIFOSIZE (HDA_ADSP_LOADER_BASE + 0x10)
+#define SKL_ADSP_REG_CL_SD_FORMAT (HDA_ADSP_LOADER_BASE + 0x12)
+#define SKL_ADSP_REG_CL_SD_FIFOL (HDA_ADSP_LOADER_BASE + 0x14)
+#define SKL_ADSP_REG_CL_SD_BDLPL (HDA_ADSP_LOADER_BASE + 0x18)
+#define SKL_ADSP_REG_CL_SD_BDLPU (HDA_ADSP_LOADER_BASE + 0x1c)
+
+/* CL: Software Position Based FIFO Capability Registers */
+#define SKL_ADSP_REG_CL_SPBFIFO (HDA_ADSP_LOADER_BASE + 0x20)
+#define SKL_ADSP_REG_CL_SPBFIFO_SPBFCH (SKL_ADSP_REG_CL_SPBFIFO + 0x0)
+#define SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL (SKL_ADSP_REG_CL_SPBFIFO + 0x4)
+#define SKL_ADSP_REG_CL_SPBFIFO_SPIB (SKL_ADSP_REG_CL_SPBFIFO + 0x8)
+#define SKL_ADSP_REG_CL_SPBFIFO_MAXFIFOS (SKL_ADSP_REG_CL_SPBFIFO + 0xc)
+
+/* CL: Stream Descriptor x Control */
+
+/* Stream Reset */
+#define CL_SD_CTL_SRST_SHIFT 0
+#define CL_SD_CTL_SRST_MASK (1 << CL_SD_CTL_SRST_SHIFT)
+#define CL_SD_CTL_SRST(x) \
+ ((x << CL_SD_CTL_SRST_SHIFT) & CL_SD_CTL_SRST_MASK)
+
+/* Stream Run */
+#define CL_SD_CTL_RUN_SHIFT 1
+#define CL_SD_CTL_RUN_MASK (1 << CL_SD_CTL_RUN_SHIFT)
+#define CL_SD_CTL_RUN(x) \
+ ((x << CL_SD_CTL_RUN_SHIFT) & CL_SD_CTL_RUN_MASK)
+
+/* Interrupt On Completion Enable */
+#define CL_SD_CTL_IOCE_SHIFT 2
+#define CL_SD_CTL_IOCE_MASK (1 << CL_SD_CTL_IOCE_SHIFT)
+#define CL_SD_CTL_IOCE(x) \
+ ((x << CL_SD_CTL_IOCE_SHIFT) & CL_SD_CTL_IOCE_MASK)
+
+/* FIFO Error Interrupt Enable */
+#define CL_SD_CTL_FEIE_SHIFT 3
+#define CL_SD_CTL_FEIE_MASK (1 << CL_SD_CTL_FEIE_SHIFT)
+#define CL_SD_CTL_FEIE(x) \
+ ((x << CL_SD_CTL_FEIE_SHIFT) & CL_SD_CTL_FEIE_MASK)
+
+/* Descriptor Error Interrupt Enable */
+#define CL_SD_CTL_DEIE_SHIFT 4
+#define CL_SD_CTL_DEIE_MASK (1 << CL_SD_CTL_DEIE_SHIFT)
+#define CL_SD_CTL_DEIE(x) \
+ ((x << CL_SD_CTL_DEIE_SHIFT) & CL_SD_CTL_DEIE_MASK)
+
+/* FIFO Limit Change */
+#define CL_SD_CTL_FIFOLC_SHIFT 5
+#define CL_SD_CTL_FIFOLC_MASK (1 << CL_SD_CTL_FIFOLC_SHIFT)
+#define CL_SD_CTL_FIFOLC(x) \
+ ((x << CL_SD_CTL_FIFOLC_SHIFT) & CL_SD_CTL_FIFOLC_MASK)
+
+/* Stripe Control */
+#define CL_SD_CTL_STRIPE_SHIFT 16
+#define CL_SD_CTL_STRIPE_MASK (0x3 << CL_SD_CTL_STRIPE_SHIFT)
+#define CL_SD_CTL_STRIPE(x) \
+ ((x << CL_SD_CTL_STRIPE_SHIFT) & CL_SD_CTL_STRIPE_MASK)
+
+/* Traffic Priority */
+#define CL_SD_CTL_TP_SHIFT 18
+#define CL_SD_CTL_TP_MASK (1 << CL_SD_CTL_TP_SHIFT)
+#define CL_SD_CTL_TP(x) \
+ ((x << CL_SD_CTL_TP_SHIFT) & CL_SD_CTL_TP_MASK)
+
+/* Bidirectional Direction Control */
+#define CL_SD_CTL_DIR_SHIFT 19
+#define CL_SD_CTL_DIR_MASK (1 << CL_SD_CTL_DIR_SHIFT)
+#define CL_SD_CTL_DIR(x) \
+ ((x << CL_SD_CTL_DIR_SHIFT) & CL_SD_CTL_DIR_MASK)
+
+/* Stream Number */
+#define CL_SD_CTL_STRM_SHIFT 20
+#define CL_SD_CTL_STRM_MASK (0xf << CL_SD_CTL_STRM_SHIFT)
+#define CL_SD_CTL_STRM(x) \
+ ((x << CL_SD_CTL_STRM_SHIFT) & CL_SD_CTL_STRM_MASK)
+
+/* CL: Stream Descriptor x Status */
+
+/* Buffer Completion Interrupt Status */
+#define CL_SD_STS_BCIS(x) CL_SD_CTL_IOCE(x)
+
+/* FIFO Error */
+#define CL_SD_STS_FIFOE(x) CL_SD_CTL_FEIE(x)
+
+/* Descriptor Error */
+#define CL_SD_STS_DESE(x) CL_SD_CTL_DEIE(x)
+
+/* FIFO Ready */
+#define CL_SD_STS_FIFORDY(x) CL_SD_CTL_FIFOLC(x)
+
+
+/* CL: Stream Descriptor x Last Valid Index */
+#define CL_SD_LVI_SHIFT 0
+#define CL_SD_LVI_MASK (0xff << CL_SD_LVI_SHIFT)
+#define CL_SD_LVI(x) ((x << CL_SD_LVI_SHIFT) & CL_SD_LVI_MASK)
+
+/* CL: Stream Descriptor x FIFO Eviction Watermark */
+#define CL_SD_FIFOW_SHIFT 0
+#define CL_SD_FIFOW_MASK (0x7 << CL_SD_FIFOW_SHIFT)
+#define CL_SD_FIFOW(x) \
+ ((x << CL_SD_FIFOW_SHIFT) & CL_SD_FIFOW_MASK)
+
+/* CL: Stream Descriptor x Buffer Descriptor List Pointer Lower Base Address */
+
+/* Protect Bits */
+#define CL_SD_BDLPLBA_PROT_SHIFT 0
+#define CL_SD_BDLPLBA_PROT_MASK (1 << CL_SD_BDLPLBA_PROT_SHIFT)
+#define CL_SD_BDLPLBA_PROT(x) \
+ ((x << CL_SD_BDLPLBA_PROT_SHIFT) & CL_SD_BDLPLBA_PROT_MASK)
+
+/* Buffer Descriptor List Lower Base Address */
+#define CL_SD_BDLPLBA_SHIFT 7
+#define CL_SD_BDLPLBA_MASK (0x1ffffff << CL_SD_BDLPLBA_SHIFT)
+#define CL_SD_BDLPLBA(x) \
+ ((BDL_ALIGN(lower_32_bits(x)) << CL_SD_BDLPLBA_SHIFT) & CL_SD_BDLPLBA_MASK)
+
+/* Buffer Descriptor List Upper Base Address */
+#define CL_SD_BDLPUBA_SHIFT 0
+#define CL_SD_BDLPUBA_MASK (0xffffffff << CL_SD_BDLPUBA_SHIFT)
+#define CL_SD_BDLPUBA(x) \
+ ((upper_32_bits(x) << CL_SD_BDLPUBA_SHIFT) & CL_SD_BDLPUBA_MASK)
+
+/*
+ * Code Loader - Software Position Based FIFO
+ * Capability Registers x Software Position Based FIFO Header
+ */
+
+/* Next Capability Pointer */
+#define CL_SPBFIFO_SPBFCH_PTR_SHIFT 0
+#define CL_SPBFIFO_SPBFCH_PTR_MASK (0xff << CL_SPBFIFO_SPBFCH_PTR_SHIFT)
+#define CL_SPBFIFO_SPBFCH_PTR(x) \
+ ((x << CL_SPBFIFO_SPBFCH_PTR_SHIFT) & CL_SPBFIFO_SPBFCH_PTR_MASK)
+
+/* Capability Identifier */
+#define CL_SPBFIFO_SPBFCH_ID_SHIFT 16
+#define CL_SPBFIFO_SPBFCH_ID_MASK (0xfff << CL_SPBFIFO_SPBFCH_ID_SHIFT)
+#define CL_SPBFIFO_SPBFCH_ID(x) \
+ ((x << CL_SPBFIFO_SPBFCH_ID_SHIFT) & CL_SPBFIFO_SPBFCH_ID_MASK)
+
+/* Capability Version */
+#define CL_SPBFIFO_SPBFCH_VER_SHIFT 28
+#define CL_SPBFIFO_SPBFCH_VER_MASK (0xf << CL_SPBFIFO_SPBFCH_VER_SHIFT)
+#define CL_SPBFIFO_SPBFCH_VER(x) \
+ ((x << CL_SPBFIFO_SPBFCH_VER_SHIFT) & CL_SPBFIFO_SPBFCH_VER_MASK)
+
+/* Software Position in Buffer Enable */
+#define CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0
+#define CL_SPBFIFO_SPBFCCTL_SPIBE_MASK (1 << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT)
+#define CL_SPBFIFO_SPBFCCTL_SPIBE(x) \
+ ((x << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & CL_SPBFIFO_SPBFCCTL_SPIBE_MASK)
+
+/* SST IPC SKL defines */
+#define SKL_WAIT_TIMEOUT 500 /* 500 msec */
+#define SKL_MAX_BUFFER_SIZE (32 * PAGE_SIZE)
+
+enum skl_cl_dma_wake_states {
+ SKL_CL_DMA_STATUS_NONE = 0,
+ SKL_CL_DMA_BUF_COMPLETE,
+ SKL_CL_DMA_ERR, /* TODO: Expand the error states */
+};
+
+struct sst_dsp;
+
+struct skl_cl_dev_ops {
+ void (*cl_setup_bdle)(struct sst_dsp *ctx,
+ struct snd_dma_buffer *dmab_data,
+ u32 **bdlp, int size, int with_ioc);
+ void (*cl_setup_controller)(struct sst_dsp *ctx,
+ struct snd_dma_buffer *dmab_bdl,
+ unsigned int max_size, u32 page_count);
+ void (*cl_setup_spb)(struct sst_dsp *ctx,
+ unsigned int size, bool enable);
+ void (*cl_cleanup_spb)(struct sst_dsp *ctx);
+ void (*cl_trigger)(struct sst_dsp *ctx, bool enable);
+ void (*cl_cleanup_controller)(struct sst_dsp *ctx);
+ int (*cl_copy_to_dmabuf)(struct sst_dsp *ctx,
+ const void *bin, u32 size);
+ void (*cl_stop_dma)(struct sst_dsp *ctx);
+};
+
+/**
+ * skl_cl_dev - holds information for code loader dma transfer
+ *
+ * @dmab_data: buffer pointer
+ * @dmab_bdl: buffer descriptor list
+ * @bufsize: ring buffer size
+ * @frags: Last valid buffer descriptor index in the BDL
+ * @curr_spib_pos: Current position in ring buffer
+ * @dma_buffer_offset: dma buffer offset
+ * @ops: operations supported on CL dma
+ * @wait_queue: wait queue to wake for wake event
+ * @wake_status: DMA wake status
+ * @wait_condition: condition to wait on wait queue
+ * @cl_dma_lock: for synchronized access to cldma
+ */
+struct skl_cl_dev {
+ struct snd_dma_buffer dmab_data;
+ struct snd_dma_buffer dmab_bdl;
+
+ unsigned int bufsize;
+ unsigned int frags;
+
+ unsigned int curr_spib_pos;
+ unsigned int dma_buffer_offset;
+ struct skl_cl_dev_ops ops;
+
+ wait_queue_head_t wait_queue;
+ int wake_status;
+ bool wait_condition;
+};
+
+#endif /* SKL_SST_CLDMA_H_ */
diff --git a/kernel/sound/soc/intel/skylake/skl-sst-dsp.c b/kernel/sound/soc/intel/skylake/skl-sst-dsp.c
new file mode 100644
index 000000000..1bfb7f63b
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -0,0 +1,347 @@
+/*
+ * skl-sst-dsp.c - SKL SST library generic function
+ *
+ * Copyright (C) 2014-15, Intel Corporation.
+ * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
+ * Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <sound/pcm.h>
+
+#include "../common/sst-dsp.h"
+#include "../common/sst-ipc.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-ipc.h"
+
+/* various timeout values */
+#define SKL_DSP_PU_TO 50
+#define SKL_DSP_PD_TO 50
+#define SKL_DSP_RESET_TO 50
+
+void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
+{
+ mutex_lock(&ctx->mutex);
+ ctx->sst_state = state;
+ mutex_unlock(&ctx->mutex);
+}
+
+static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
+{
+ int ret;
+
+ /* update bits */
+ sst_dsp_shim_update_bits_unlocked(ctx,
+ SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK,
+ SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK));
+
+ /* poll with timeout to check if operation successful */
+ ret = sst_dsp_register_poll(ctx,
+ SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CRST_MASK,
+ SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK),
+ SKL_DSP_RESET_TO,
+ "Set reset");
+ if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
+ SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) !=
+ SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) {
+ dev_err(ctx->dev, "Set reset state failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
+{
+ int ret;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* update bits */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CRST_MASK, 0);
+
+ /* poll with timeout to check if operation successful */
+ ret = sst_dsp_register_poll(ctx,
+ SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CRST_MASK,
+ 0,
+ SKL_DSP_RESET_TO,
+ "Unset reset");
+
+ if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
+ SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) {
+ dev_err(ctx->dev, "Unset reset state failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
+{
+ int val;
+ bool is_enable;
+
+ val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
+
+ is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) &&
+ (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) &&
+ !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) &&
+ !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)));
+
+ dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
+ return is_enable;
+}
+
+static int skl_dsp_reset_core(struct sst_dsp *ctx)
+{
+ /* stall core */
+ sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
+ SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
+
+ /* set reset state */
+ return skl_dsp_core_set_reset_state(ctx);
+}
+
+static int skl_dsp_start_core(struct sst_dsp *ctx)
+{
+ int ret;
+
+ /* unset reset state */
+ ret = skl_dsp_core_unset_reset_state(ctx);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "dsp unset reset fails\n");
+ return ret;
+ }
+
+ /* run core */
+ dev_dbg(ctx->dev, "run core...\n");
+ sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
+ ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
+
+ if (!is_skl_dsp_core_enable(ctx)) {
+ skl_dsp_reset_core(ctx);
+ dev_err(ctx->dev, "DSP core enable failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int skl_dsp_core_power_up(struct sst_dsp *ctx)
+{
+ int ret;
+
+ /* update bits */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK));
+
+ /* poll with timeout to check if operation successful */
+ ret = sst_dsp_register_poll(ctx,
+ SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CPA_MASK,
+ SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK),
+ SKL_DSP_PU_TO,
+ "Power up");
+
+ if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
+ SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) !=
+ SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) {
+ dev_err(ctx->dev, "DSP core power up failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int skl_dsp_core_power_down(struct sst_dsp *ctx)
+{
+ /* update bits */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_SPA_MASK, 0);
+
+ /* poll with timeout to check if operation successful */
+ return sst_dsp_register_poll(ctx,
+ SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CPA_MASK,
+ 0,
+ SKL_DSP_PD_TO,
+ "Power down");
+}
+
+static int skl_dsp_enable_core(struct sst_dsp *ctx)
+{
+ int ret;
+
+ /* power up */
+ ret = skl_dsp_core_power_up(ctx);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "dsp core power up failed\n");
+ return ret;
+ }
+
+ return skl_dsp_start_core(ctx);
+}
+
+int skl_dsp_disable_core(struct sst_dsp *ctx)
+{
+ int ret;
+
+ ret = skl_dsp_reset_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core reset failed\n");
+ return ret;
+ }
+
+ /* power down core*/
+ ret = skl_dsp_core_power_down(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core power down failed\n");
+ return ret;
+ }
+
+ if (is_skl_dsp_core_enable(ctx)) {
+ dev_err(ctx->dev, "DSP core disable failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+int skl_dsp_boot(struct sst_dsp *ctx)
+{
+ int ret;
+
+ if (is_skl_dsp_core_enable(ctx)) {
+ dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
+ ret = skl_dsp_reset_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp reset failed\n");
+ return ret;
+ }
+
+ ret = skl_dsp_start_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp start failed\n");
+ return ret;
+ }
+ } else {
+ dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
+ ret = skl_dsp_disable_core(ctx);
+
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp disable core failes\n");
+ return ret;
+ }
+ ret = skl_dsp_enable_core(ctx);
+ }
+
+ return ret;
+}
+
+irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
+{
+ struct sst_dsp *ctx = dev_id;
+ u32 val;
+ irqreturn_t result = IRQ_NONE;
+
+ spin_lock(&ctx->spinlock);
+
+ val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
+ ctx->intr_status = val;
+
+ if (val == 0xffffffff) {
+ spin_unlock(&ctx->spinlock);
+ return IRQ_NONE;
+ }
+
+ if (val & SKL_ADSPIS_IPC) {
+ skl_ipc_int_disable(ctx);
+ result = IRQ_WAKE_THREAD;
+ }
+
+ if (val & SKL_ADSPIS_CL_DMA) {
+ skl_cldma_int_disable(ctx);
+ result = IRQ_WAKE_THREAD;
+ }
+
+ spin_unlock(&ctx->spinlock);
+
+ return result;
+}
+
+int skl_dsp_wake(struct sst_dsp *ctx)
+{
+ return ctx->fw_ops.set_state_D0(ctx);
+}
+EXPORT_SYMBOL_GPL(skl_dsp_wake);
+
+int skl_dsp_sleep(struct sst_dsp *ctx)
+{
+ return ctx->fw_ops.set_state_D3(ctx);
+}
+EXPORT_SYMBOL_GPL(skl_dsp_sleep);
+
+struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
+ struct sst_dsp_device *sst_dev, int irq)
+{
+ int ret;
+ struct sst_dsp *sst;
+
+ sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
+ if (sst == NULL)
+ return NULL;
+
+ spin_lock_init(&sst->spinlock);
+ mutex_init(&sst->mutex);
+ sst->dev = dev;
+ sst->sst_dev = sst_dev;
+ sst->irq = irq;
+ sst->ops = sst_dev->ops;
+ sst->thread_context = sst_dev->thread_context;
+
+ /* Initialise SST Audio DSP */
+ if (sst->ops->init) {
+ ret = sst->ops->init(sst, NULL);
+ if (ret < 0)
+ return NULL;
+ }
+
+ /* Register the ISR */
+ ret = request_threaded_irq(sst->irq, sst->ops->irq_handler,
+ sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
+ if (ret) {
+ dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n",
+ sst->irq);
+ return NULL;
+ }
+
+ return sst;
+}
+
+void skl_dsp_free(struct sst_dsp *dsp)
+{
+ skl_ipc_int_disable(dsp);
+
+ free_irq(dsp->irq, dsp);
+ skl_dsp_disable_core(dsp);
+}
+EXPORT_SYMBOL_GPL(skl_dsp_free);
+
+bool is_skl_dsp_running(struct sst_dsp *ctx)
+{
+ return (ctx->sst_state == SKL_DSP_RUNNING);
+}
+EXPORT_SYMBOL_GPL(is_skl_dsp_running);
diff --git a/kernel/sound/soc/intel/skylake/skl-sst-dsp.h b/kernel/sound/soc/intel/skylake/skl-sst-dsp.h
new file mode 100644
index 000000000..6bfcef449
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -0,0 +1,145 @@
+/*
+ * Skylake SST DSP Support
+ *
+ * Copyright (C) 2014-15, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __SKL_SST_DSP_H__
+#define __SKL_SST_DSP_H__
+
+#include <linux/interrupt.h>
+#include <sound/memalloc.h>
+#include "skl-sst-cldma.h"
+
+struct sst_dsp;
+struct skl_sst;
+struct sst_dsp_device;
+
+/* Intel HD Audio General DSP Registers */
+#define SKL_ADSP_GEN_BASE 0x0
+#define SKL_ADSP_REG_ADSPCS (SKL_ADSP_GEN_BASE + 0x04)
+#define SKL_ADSP_REG_ADSPIC (SKL_ADSP_GEN_BASE + 0x08)
+#define SKL_ADSP_REG_ADSPIS (SKL_ADSP_GEN_BASE + 0x0C)
+#define SKL_ADSP_REG_ADSPIC2 (SKL_ADSP_GEN_BASE + 0x10)
+#define SKL_ADSP_REG_ADSPIS2 (SKL_ADSP_GEN_BASE + 0x14)
+
+/* Intel HD Audio Inter-Processor Communication Registers */
+#define SKL_ADSP_IPC_BASE 0x40
+#define SKL_ADSP_REG_HIPCT (SKL_ADSP_IPC_BASE + 0x00)
+#define SKL_ADSP_REG_HIPCTE (SKL_ADSP_IPC_BASE + 0x04)
+#define SKL_ADSP_REG_HIPCI (SKL_ADSP_IPC_BASE + 0x08)
+#define SKL_ADSP_REG_HIPCIE (SKL_ADSP_IPC_BASE + 0x0C)
+#define SKL_ADSP_REG_HIPCCTL (SKL_ADSP_IPC_BASE + 0x10)
+
+/* HIPCI */
+#define SKL_ADSP_REG_HIPCI_BUSY BIT(31)
+
+/* HIPCIE */
+#define SKL_ADSP_REG_HIPCIE_DONE BIT(30)
+
+/* HIPCCTL */
+#define SKL_ADSP_REG_HIPCCTL_DONE BIT(1)
+#define SKL_ADSP_REG_HIPCCTL_BUSY BIT(0)
+
+/* HIPCT */
+#define SKL_ADSP_REG_HIPCT_BUSY BIT(31)
+
+/* Intel HD Audio SRAM Window 1 */
+#define SKL_ADSP_SRAM1_BASE 0xA000
+
+#define SKL_ADSP_MMIO_LEN 0x10000
+
+#define SKL_ADSP_W0_STAT_SZ 0x800
+
+#define SKL_ADSP_W0_UP_SZ 0x800
+
+#define SKL_ADSP_W1_SZ 0x1000
+
+#define SKL_FW_STS_MASK 0xf
+
+#define SKL_FW_INIT 0x1
+#define SKL_FW_RFW_START 0xf
+
+#define SKL_ADSPIC_IPC 1
+#define SKL_ADSPIS_IPC 1
+
+/* ADSPCS - Audio DSP Control & Status */
+#define SKL_DSP_CORES 1
+#define SKL_DSP_CORE0_MASK 1
+#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1)
+
+/* Core Reset - asserted high */
+#define SKL_ADSPCS_CRST_SHIFT 0
+#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT)
+#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK)
+
+/* Core run/stall - when set to '1' core is stalled */
+#define SKL_ADSPCS_CSTALL_SHIFT 8
+#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \
+ SKL_ADSPCS_CSTALL_SHIFT)
+#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \
+ SKL_ADSPCS_CSTALL_MASK)
+
+/* Set Power Active - when set to '1' turn cores on */
+#define SKL_ADSPCS_SPA_SHIFT 16
+#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT)
+#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK)
+
+/* Current Power Active - power status of cores, set by hardware */
+#define SKL_ADSPCS_CPA_SHIFT 24
+#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT)
+#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK)
+
+#define SST_DSP_POWER_D0 0x0 /* full On */
+#define SST_DSP_POWER_D3 0x3 /* Off */
+
+enum skl_dsp_states {
+ SKL_DSP_RUNNING = 1,
+ SKL_DSP_RESET,
+};
+
+struct skl_dsp_fw_ops {
+ int (*load_fw)(struct sst_dsp *ctx);
+ /* FW module parser/loader */
+ int (*parse_fw)(struct sst_dsp *ctx);
+ int (*set_state_D0)(struct sst_dsp *ctx);
+ int (*set_state_D3)(struct sst_dsp *ctx);
+ unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
+};
+
+struct skl_dsp_loader_ops {
+ int (*alloc_dma_buf)(struct device *dev,
+ struct snd_dma_buffer *dmab, size_t size);
+ int (*free_dma_buf)(struct device *dev,
+ struct snd_dma_buffer *dmab);
+};
+
+void skl_cldma_process_intr(struct sst_dsp *ctx);
+void skl_cldma_int_disable(struct sst_dsp *ctx);
+int skl_cldma_prepare(struct sst_dsp *ctx);
+
+void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
+struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
+ struct sst_dsp_device *sst_dev, int irq);
+int skl_dsp_disable_core(struct sst_dsp *ctx);
+bool is_skl_dsp_running(struct sst_dsp *ctx);
+irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
+int skl_dsp_wake(struct sst_dsp *ctx);
+int skl_dsp_sleep(struct sst_dsp *ctx);
+void skl_dsp_free(struct sst_dsp *dsp);
+
+int skl_dsp_boot(struct sst_dsp *ctx);
+int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+ struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp);
+void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
+
+#endif /*__SKL_SST_DSP_H__*/
diff --git a/kernel/sound/soc/intel/skylake/skl-sst-ipc.c b/kernel/sound/soc/intel/skylake/skl-sst-ipc.c
new file mode 100644
index 000000000..3345ea0d4
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -0,0 +1,783 @@
+/*
+ * skl-sst-ipc.c - Intel skl IPC Support
+ *
+ * Copyright (C) 2014-15, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+
+
+#define IPC_IXC_STATUS_BITS 24
+
+/* Global Message - Generic */
+#define IPC_GLB_TYPE_SHIFT 24
+#define IPC_GLB_TYPE_MASK (0xf << IPC_GLB_TYPE_SHIFT)
+#define IPC_GLB_TYPE(x) ((x) << IPC_GLB_TYPE_SHIFT)
+
+/* Global Message - Reply */
+#define IPC_GLB_REPLY_STATUS_SHIFT 24
+#define IPC_GLB_REPLY_STATUS_MASK ((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1)
+#define IPC_GLB_REPLY_STATUS(x) ((x) << IPC_GLB_REPLY_STATUS_SHIFT)
+
+#define IPC_TIMEOUT_MSECS 3000
+
+#define IPC_EMPTY_LIST_SIZE 8
+
+#define IPC_MSG_TARGET_SHIFT 30
+#define IPC_MSG_TARGET_MASK 0x1
+#define IPC_MSG_TARGET(x) (((x) & IPC_MSG_TARGET_MASK) \
+ << IPC_MSG_TARGET_SHIFT)
+
+#define IPC_MSG_DIR_SHIFT 29
+#define IPC_MSG_DIR_MASK 0x1
+#define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \
+ << IPC_MSG_DIR_SHIFT)
+/* Global Notification Message */
+#define IPC_GLB_NOTIFY_TYPE_SHIFT 16
+#define IPC_GLB_NOTIFY_TYPE_MASK 0xFF
+#define IPC_GLB_NOTIFY_TYPE(x) (((x) >> IPC_GLB_NOTIFY_TYPE_SHIFT) \
+ & IPC_GLB_NOTIFY_TYPE_MASK)
+
+#define IPC_GLB_NOTIFY_MSG_TYPE_SHIFT 24
+#define IPC_GLB_NOTIFY_MSG_TYPE_MASK 0x1F
+#define IPC_GLB_NOTIFY_MSG_TYPE(x) (((x) >> IPC_GLB_NOTIFY_MSG_TYPE_SHIFT) \
+ & IPC_GLB_NOTIFY_MSG_TYPE_MASK)
+
+#define IPC_GLB_NOTIFY_RSP_SHIFT 29
+#define IPC_GLB_NOTIFY_RSP_MASK 0x1
+#define IPC_GLB_NOTIFY_RSP_TYPE(x) (((x) >> IPC_GLB_NOTIFY_RSP_SHIFT) \
+ & IPC_GLB_NOTIFY_RSP_MASK)
+
+/* Pipeline operations */
+
+/* Create pipeline message */
+#define IPC_PPL_MEM_SIZE_SHIFT 0
+#define IPC_PPL_MEM_SIZE_MASK 0x7FF
+#define IPC_PPL_MEM_SIZE(x) (((x) & IPC_PPL_MEM_SIZE_MASK) \
+ << IPC_PPL_MEM_SIZE_SHIFT)
+
+#define IPC_PPL_TYPE_SHIFT 11
+#define IPC_PPL_TYPE_MASK 0x1F
+#define IPC_PPL_TYPE(x) (((x) & IPC_PPL_TYPE_MASK) \
+ << IPC_PPL_TYPE_SHIFT)
+
+#define IPC_INSTANCE_ID_SHIFT 16
+#define IPC_INSTANCE_ID_MASK 0xFF
+#define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \
+ << IPC_INSTANCE_ID_SHIFT)
+
+/* Set pipeline state message */
+#define IPC_PPL_STATE_SHIFT 0
+#define IPC_PPL_STATE_MASK 0x1F
+#define IPC_PPL_STATE(x) (((x) & IPC_PPL_STATE_MASK) \
+ << IPC_PPL_STATE_SHIFT)
+
+/* Module operations primary register */
+#define IPC_MOD_ID_SHIFT 0
+#define IPC_MOD_ID_MASK 0xFFFF
+#define IPC_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \
+ << IPC_MOD_ID_SHIFT)
+
+#define IPC_MOD_INSTANCE_ID_SHIFT 16
+#define IPC_MOD_INSTANCE_ID_MASK 0xFF
+#define IPC_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \
+ << IPC_MOD_INSTANCE_ID_SHIFT)
+
+/* Init instance message extension register */
+#define IPC_PARAM_BLOCK_SIZE_SHIFT 0
+#define IPC_PARAM_BLOCK_SIZE_MASK 0xFFFF
+#define IPC_PARAM_BLOCK_SIZE(x) (((x) & IPC_PARAM_BLOCK_SIZE_MASK) \
+ << IPC_PARAM_BLOCK_SIZE_SHIFT)
+
+#define IPC_PPL_INSTANCE_ID_SHIFT 16
+#define IPC_PPL_INSTANCE_ID_MASK 0xFF
+#define IPC_PPL_INSTANCE_ID(x) (((x) & IPC_PPL_INSTANCE_ID_MASK) \
+ << IPC_PPL_INSTANCE_ID_SHIFT)
+
+#define IPC_CORE_ID_SHIFT 24
+#define IPC_CORE_ID_MASK 0x1F
+#define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \
+ << IPC_CORE_ID_SHIFT)
+
+/* Bind/Unbind message extension register */
+#define IPC_DST_MOD_ID_SHIFT 0
+#define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \
+ << IPC_DST_MOD_ID_SHIFT)
+
+#define IPC_DST_MOD_INSTANCE_ID_SHIFT 16
+#define IPC_DST_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \
+ << IPC_DST_MOD_INSTANCE_ID_SHIFT)
+
+#define IPC_DST_QUEUE_SHIFT 24
+#define IPC_DST_QUEUE_MASK 0x7
+#define IPC_DST_QUEUE(x) (((x) & IPC_DST_QUEUE_MASK) \
+ << IPC_DST_QUEUE_SHIFT)
+
+#define IPC_SRC_QUEUE_SHIFT 27
+#define IPC_SRC_QUEUE_MASK 0x7
+#define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \
+ << IPC_SRC_QUEUE_SHIFT)
+
+/* Save pipeline messgae extension register */
+#define IPC_DMA_ID_SHIFT 0
+#define IPC_DMA_ID_MASK 0x1F
+#define IPC_DMA_ID(x) (((x) & IPC_DMA_ID_MASK) \
+ << IPC_DMA_ID_SHIFT)
+/* Large Config message extension register */
+#define IPC_DATA_OFFSET_SZ_SHIFT 0
+#define IPC_DATA_OFFSET_SZ_MASK 0xFFFFF
+#define IPC_DATA_OFFSET_SZ(x) (((x) & IPC_DATA_OFFSET_SZ_MASK) \
+ << IPC_DATA_OFFSET_SZ_SHIFT)
+#define IPC_DATA_OFFSET_SZ_CLEAR ~(IPC_DATA_OFFSET_SZ_MASK \
+ << IPC_DATA_OFFSET_SZ_SHIFT)
+
+#define IPC_LARGE_PARAM_ID_SHIFT 20
+#define IPC_LARGE_PARAM_ID_MASK 0xFF
+#define IPC_LARGE_PARAM_ID(x) (((x) & IPC_LARGE_PARAM_ID_MASK) \
+ << IPC_LARGE_PARAM_ID_SHIFT)
+
+#define IPC_FINAL_BLOCK_SHIFT 28
+#define IPC_FINAL_BLOCK_MASK 0x1
+#define IPC_FINAL_BLOCK(x) (((x) & IPC_FINAL_BLOCK_MASK) \
+ << IPC_FINAL_BLOCK_SHIFT)
+
+#define IPC_INITIAL_BLOCK_SHIFT 29
+#define IPC_INITIAL_BLOCK_MASK 0x1
+#define IPC_INITIAL_BLOCK(x) (((x) & IPC_INITIAL_BLOCK_MASK) \
+ << IPC_INITIAL_BLOCK_SHIFT)
+#define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \
+ << IPC_INITIAL_BLOCK_SHIFT)
+
+enum skl_ipc_msg_target {
+ IPC_FW_GEN_MSG = 0,
+ IPC_MOD_MSG = 1
+};
+
+enum skl_ipc_msg_direction {
+ IPC_MSG_REQUEST = 0,
+ IPC_MSG_REPLY = 1
+};
+
+/* Global Message Types */
+enum skl_ipc_glb_type {
+ IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */
+ IPC_GLB_LOAD_MULTIPLE_MODS = 15,
+ IPC_GLB_UNLOAD_MULTIPLE_MODS = 16,
+ IPC_GLB_CREATE_PPL = 17,
+ IPC_GLB_DELETE_PPL = 18,
+ IPC_GLB_SET_PPL_STATE = 19,
+ IPC_GLB_GET_PPL_STATE = 20,
+ IPC_GLB_GET_PPL_CONTEXT_SIZE = 21,
+ IPC_GLB_SAVE_PPL = 22,
+ IPC_GLB_RESTORE_PPL = 23,
+ IPC_GLB_NOTIFY = 26,
+ IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */
+};
+
+enum skl_ipc_glb_reply {
+ IPC_GLB_REPLY_SUCCESS = 0,
+
+ IPC_GLB_REPLY_UNKNOWN_MSG_TYPE = 1,
+ IPC_GLB_REPLY_ERROR_INVALID_PARAM = 2,
+
+ IPC_GLB_REPLY_BUSY = 3,
+ IPC_GLB_REPLY_PENDING = 4,
+ IPC_GLB_REPLY_FAILURE = 5,
+ IPC_GLB_REPLY_INVALID_REQUEST = 6,
+
+ IPC_GLB_REPLY_OUT_OF_MEMORY = 7,
+ IPC_GLB_REPLY_OUT_OF_MIPS = 8,
+
+ IPC_GLB_REPLY_INVALID_RESOURCE_ID = 9,
+ IPC_GLB_REPLY_INVALID_RESOURCE_STATE = 10,
+
+ IPC_GLB_REPLY_MOD_MGMT_ERROR = 100,
+ IPC_GLB_REPLY_MOD_LOAD_CL_FAILED = 101,
+ IPC_GLB_REPLY_MOD_LOAD_INVALID_HASH = 102,
+
+ IPC_GLB_REPLY_MOD_UNLOAD_INST_EXIST = 103,
+ IPC_GLB_REPLY_MOD_NOT_INITIALIZED = 104,
+
+ IPC_GLB_REPLY_INVALID_CONFIG_PARAM_ID = 120,
+ IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121,
+ IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140,
+ IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141,
+
+ IPC_GLB_REPLY_PPL_NOT_INITIALIZED = 160,
+ IPC_GLB_REPLY_PPL_NOT_EXIST = 161,
+ IPC_GLB_REPLY_PPL_SAVE_FAILED = 162,
+ IPC_GLB_REPLY_PPL_RESTORE_FAILED = 163,
+
+ IPC_MAX_STATUS = ((1<<IPC_IXC_STATUS_BITS)-1)
+};
+
+enum skl_ipc_notification_type {
+ IPC_GLB_NOTIFY_GLITCH = 0,
+ IPC_GLB_NOTIFY_OVERRUN = 1,
+ IPC_GLB_NOTIFY_UNDERRUN = 2,
+ IPC_GLB_NOTIFY_END_STREAM = 3,
+ IPC_GLB_NOTIFY_PHRASE_DETECTED = 4,
+ IPC_GLB_NOTIFY_RESOURCE_EVENT = 5,
+ IPC_GLB_NOTIFY_LOG_BUFFER_STATUS = 6,
+ IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7,
+ IPC_GLB_NOTIFY_FW_READY = 8
+};
+
+/* Module Message Types */
+enum skl_ipc_module_msg {
+ IPC_MOD_INIT_INSTANCE = 0,
+ IPC_MOD_CONFIG_GET = 1,
+ IPC_MOD_CONFIG_SET = 2,
+ IPC_MOD_LARGE_CONFIG_GET = 3,
+ IPC_MOD_LARGE_CONFIG_SET = 4,
+ IPC_MOD_BIND = 5,
+ IPC_MOD_UNBIND = 6,
+ IPC_MOD_SET_DX = 7
+};
+
+static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data,
+ size_t tx_size)
+{
+ if (tx_size)
+ memcpy(msg->tx_data, tx_data, tx_size);
+}
+
+static bool skl_ipc_is_dsp_busy(struct sst_dsp *dsp)
+{
+ u32 hipci;
+
+ hipci = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCI);
+ return (hipci & SKL_ADSP_REG_HIPCI_BUSY);
+}
+
+/* Lock to be held by caller */
+static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+ struct skl_ipc_header *header = (struct skl_ipc_header *)(&msg->header);
+
+ if (msg->tx_size)
+ sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+ sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCIE,
+ header->extension);
+ sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCI,
+ header->primary | SKL_ADSP_REG_HIPCI_BUSY);
+}
+
+static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc,
+ u64 ipc_header)
+{
+ struct ipc_message *msg = NULL;
+ struct skl_ipc_header *header = (struct skl_ipc_header *)(&ipc_header);
+
+ if (list_empty(&ipc->rx_list)) {
+ dev_err(ipc->dev, "ipc: rx list is empty but received 0x%x\n",
+ header->primary);
+ goto out;
+ }
+
+ msg = list_first_entry(&ipc->rx_list, struct ipc_message, list);
+
+out:
+ return msg;
+
+}
+
+static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
+ struct skl_ipc_header header)
+{
+ struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc);
+
+ if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) {
+ switch (IPC_GLB_NOTIFY_TYPE(header.primary)) {
+
+ case IPC_GLB_NOTIFY_UNDERRUN:
+ dev_err(ipc->dev, "FW Underrun %x\n", header.primary);
+ break;
+
+ case IPC_GLB_NOTIFY_RESOURCE_EVENT:
+ dev_err(ipc->dev, "MCPS Budget Violation: %x\n",
+ header.primary);
+ break;
+
+ case IPC_GLB_NOTIFY_FW_READY:
+ skl->boot_complete = true;
+ wake_up(&skl->boot_wait);
+ break;
+
+ default:
+ dev_err(ipc->dev, "ipc: Unhandled error msg=%x",
+ header.primary);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
+ struct skl_ipc_header header)
+{
+ struct ipc_message *msg;
+ u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK;
+ u64 *ipc_header = (u64 *)(&header);
+
+ msg = skl_ipc_reply_get_msg(ipc, *ipc_header);
+ if (msg == NULL) {
+ dev_dbg(ipc->dev, "ipc: rx list is empty\n");
+ return;
+ }
+
+ /* first process the header */
+ switch (reply) {
+ case IPC_GLB_REPLY_SUCCESS:
+ dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
+ break;
+
+ case IPC_GLB_REPLY_OUT_OF_MEMORY:
+ dev_err(ipc->dev, "ipc fw reply: %x: no memory\n", header.primary);
+ msg->errno = -ENOMEM;
+ break;
+
+ case IPC_GLB_REPLY_BUSY:
+ dev_err(ipc->dev, "ipc fw reply: %x: Busy\n", header.primary);
+ msg->errno = -EBUSY;
+ break;
+
+ default:
+ dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply);
+ msg->errno = -EINVAL;
+ break;
+ }
+
+ if (reply != IPC_GLB_REPLY_SUCCESS) {
+ dev_err(ipc->dev, "ipc FW reply: reply=%d", reply);
+ dev_err(ipc->dev, "FW Error Code: %u\n",
+ ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp));
+ }
+
+ list_del(&msg->list);
+ sst_ipc_tx_msg_reply_complete(ipc, msg);
+}
+
+irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
+{
+ struct sst_dsp *dsp = context;
+ struct skl_sst *skl = sst_dsp_get_thread_context(dsp);
+ struct sst_generic_ipc *ipc = &skl->ipc;
+ struct skl_ipc_header header = {0};
+ u32 hipcie, hipct, hipcte;
+ int ipc_irq = 0;
+
+ if (dsp->intr_status & SKL_ADSPIS_CL_DMA)
+ skl_cldma_process_intr(dsp);
+
+ /* Here we handle IPC interrupts only */
+ if (!(dsp->intr_status & SKL_ADSPIS_IPC))
+ return IRQ_NONE;
+
+ hipcie = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCIE);
+ hipct = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCT);
+
+ /* reply message from DSP */
+ if (hipcie & SKL_ADSP_REG_HIPCIE_DONE) {
+ sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+ /* clear DONE bit - tell DSP we have completed the operation */
+ sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCIE,
+ SKL_ADSP_REG_HIPCIE_DONE, SKL_ADSP_REG_HIPCIE_DONE);
+
+ ipc_irq = 1;
+
+ /* unmask Done interrupt */
+ sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE);
+ }
+
+ /* New message from DSP */
+ if (hipct & SKL_ADSP_REG_HIPCT_BUSY) {
+ hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE);
+ header.primary = hipct;
+ header.extension = hipcte;
+ dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x",
+ header.primary);
+ dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x",
+ header.extension);
+
+ if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
+ /* Handle Immediate reply from DSP Core */
+ skl_ipc_process_reply(ipc, header);
+ } else {
+ dev_dbg(dsp->dev, "IPC irq: Notification from firmware\n");
+ skl_ipc_process_notification(ipc, header);
+ }
+ /* clear busy interrupt */
+ sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCT,
+ SKL_ADSP_REG_HIPCT_BUSY, SKL_ADSP_REG_HIPCT_BUSY);
+ ipc_irq = 1;
+ }
+
+ if (ipc_irq == 0)
+ return IRQ_NONE;
+
+ skl_ipc_int_enable(dsp);
+
+ /* continue to send any remaining messages... */
+ queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+ return IRQ_HANDLED;
+}
+
+void skl_ipc_int_enable(struct sst_dsp *ctx)
+{
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_ADSPIC,
+ SKL_ADSPIC_IPC, SKL_ADSPIC_IPC);
+}
+
+void skl_ipc_int_disable(struct sst_dsp *ctx)
+{
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC,
+ SKL_ADSPIC_IPC, 0);
+}
+
+void skl_ipc_op_int_enable(struct sst_dsp *ctx)
+{
+ /* enable IPC DONE interrupt */
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE);
+
+ /* Enable IPC BUSY interrupt */
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY);
+}
+
+void skl_ipc_op_int_disable(struct sst_dsp *ctx)
+{
+ /* disable IPC DONE interrupt */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+ /* Disable IPC BUSY interrupt */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_BUSY, 0);
+
+}
+
+bool skl_ipc_int_status(struct sst_dsp *ctx)
+{
+ return sst_dsp_shim_read_unlocked(ctx,
+ SKL_ADSP_REG_ADSPIS) & SKL_ADSPIS_IPC;
+}
+
+int skl_ipc_init(struct device *dev, struct skl_sst *skl)
+{
+ struct sst_generic_ipc *ipc;
+ int err;
+
+ ipc = &skl->ipc;
+ ipc->dsp = skl->dsp;
+ ipc->dev = dev;
+
+ ipc->tx_data_max_size = SKL_ADSP_W1_SZ;
+ ipc->rx_data_max_size = SKL_ADSP_W0_UP_SZ;
+
+ err = sst_ipc_init(ipc);
+ if (err)
+ return err;
+
+ ipc->ops.tx_msg = skl_ipc_tx_msg;
+ ipc->ops.tx_data_copy = skl_ipc_tx_data_copy;
+ ipc->ops.is_dsp_busy = skl_ipc_is_dsp_busy;
+
+ return 0;
+}
+
+void skl_ipc_free(struct sst_generic_ipc *ipc)
+{
+ /* Disable IPC DONE interrupt */
+ sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+ /* Disable IPC BUSY interrupt */
+ sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_BUSY, 0);
+
+ sst_ipc_fini(ipc);
+}
+
+int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc,
+ u16 ppl_mem_size, u8 ppl_type, u8 instance_id)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_GLB_CREATE_PPL);
+ header.primary |= IPC_INSTANCE_ID(instance_id);
+ header.primary |= IPC_PPL_TYPE(ppl_type);
+ header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size);
+
+ dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: create pipeline fail, err: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_create_pipeline);
+
+int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_GLB_DELETE_PPL);
+ header.primary |= IPC_INSTANCE_ID(instance_id);
+
+ dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: delete pipeline failed, err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_delete_pipeline);
+
+int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc,
+ u8 instance_id, enum skl_ipc_pipeline_state state)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_GLB_SET_PPL_STATE);
+ header.primary |= IPC_INSTANCE_ID(instance_id);
+ header.primary |= IPC_PPL_STATE(state);
+
+ dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: set pipeline state failed, err: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_set_pipeline_state);
+
+int
+skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, u8 instance_id, int dma_id)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_GLB_SAVE_PPL);
+ header.primary |= IPC_INSTANCE_ID(instance_id);
+
+ header.extension = IPC_DMA_ID(dma_id);
+ dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: save pipeline failed, err: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_save_pipeline);
+
+int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_GLB_RESTORE_PPL);
+ header.primary |= IPC_INSTANCE_ID(instance_id);
+
+ dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: restore pipeline failed, err: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_restore_pipeline);
+
+int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id,
+ u16 module_id, struct skl_ipc_dxstate_info *dx)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_DX);
+ header.primary |= IPC_MOD_INSTANCE_ID(instance_id);
+ header.primary |= IPC_MOD_ID(module_id);
+
+ dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
+ header.primary, header.extension);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
+ dx, sizeof(dx), NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_set_dx);
+
+int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
+ struct skl_ipc_init_instance_msg *msg, void *param_data)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+ u32 *buffer = (u32 *)param_data;
+ /* param_block_size must be in dwords */
+ u16 param_block_size = msg->param_data_size / sizeof(u32);
+
+ print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+ 16, 4, buffer, param_block_size, false);
+
+ header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_MOD_INIT_INSTANCE);
+ header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
+ header.primary |= IPC_MOD_ID(msg->module_id);
+
+ header.extension = IPC_CORE_ID(msg->core_id);
+ header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id);
+ header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size);
+
+ dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
+ header.primary, header.extension);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, param_data,
+ msg->param_data_size, NULL, 0);
+
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: init instance failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_init_instance);
+
+int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
+ struct skl_ipc_bind_unbind_msg *msg)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ u8 bind_unbind = msg->bind ? IPC_MOD_BIND : IPC_MOD_UNBIND;
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(bind_unbind);
+ header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
+ header.primary |= IPC_MOD_ID(msg->module_id);
+
+ header.extension = IPC_DST_MOD_ID(msg->dst_module_id);
+ header.extension |= IPC_DST_MOD_INSTANCE_ID(msg->dst_instance_id);
+ header.extension |= IPC_DST_QUEUE(msg->dst_queue);
+ header.extension |= IPC_SRC_QUEUE(msg->src_queue);
+
+ dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary,
+ header.extension);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev, "ipc: bind/unbind faileden");
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind);
+
+int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
+ struct skl_ipc_large_config_msg *msg, u32 *param)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret = 0;
+ size_t sz_remaining, tx_size, data_offset;
+
+ header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_SET);
+ header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
+ header.primary |= IPC_MOD_ID(msg->module_id);
+
+ header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size);
+ header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id);
+ header.extension |= IPC_FINAL_BLOCK(0);
+ header.extension |= IPC_INITIAL_BLOCK(1);
+
+ sz_remaining = msg->param_data_size;
+ data_offset = 0;
+ while (sz_remaining != 0) {
+ tx_size = sz_remaining > SKL_ADSP_W1_SZ
+ ? SKL_ADSP_W1_SZ : sz_remaining;
+ if (tx_size == sz_remaining)
+ header.extension |= IPC_FINAL_BLOCK(1);
+
+ dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__,
+ header.primary, header.extension);
+ dev_dbg(ipc->dev, "transmitting offset: %#x, size: %#x\n",
+ (unsigned)data_offset, (unsigned)tx_size);
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
+ ((char *)param) + data_offset,
+ tx_size, NULL, 0);
+ if (ret < 0) {
+ dev_err(ipc->dev,
+ "ipc: set large config fail, err: %d\n", ret);
+ return ret;
+ }
+ sz_remaining -= tx_size;
+ data_offset = msg->param_data_size - sz_remaining;
+
+ /* clear the fields */
+ header.extension &= IPC_INITIAL_BLOCK_CLEAR;
+ header.extension &= IPC_DATA_OFFSET_SZ_CLEAR;
+ /* fill the fields */
+ header.extension |= IPC_INITIAL_BLOCK(0);
+ header.extension |= IPC_DATA_OFFSET_SZ(data_offset);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_set_large_config);
diff --git a/kernel/sound/soc/intel/skylake/skl-sst-ipc.h b/kernel/sound/soc/intel/skylake/skl-sst-ipc.h
new file mode 100644
index 000000000..f1a154e45
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -0,0 +1,126 @@
+/*
+ * Intel SKL IPC Support
+ *
+ * Copyright (C) 2014-15, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __SKL_IPC_H
+#define __SKL_IPC_H
+
+#include <linux/kthread.h>
+#include <linux/irqreturn.h>
+#include "../common/sst-ipc.h"
+
+struct sst_dsp;
+struct skl_sst;
+struct sst_generic_ipc;
+
+enum skl_ipc_pipeline_state {
+ PPL_INVALID_STATE = 0,
+ PPL_UNINITIALIZED = 1,
+ PPL_RESET = 2,
+ PPL_PAUSED = 3,
+ PPL_RUNNING = 4,
+ PPL_ERROR_STOP = 5,
+ PPL_SAVED = 6,
+ PPL_RESTORED = 7
+};
+
+struct skl_ipc_dxstate_info {
+ u32 core_mask;
+ u32 dx_mask;
+};
+
+struct skl_ipc_header {
+ u32 primary;
+ u32 extension;
+};
+
+struct skl_sst {
+ struct device *dev;
+ struct sst_dsp *dsp;
+
+ /* boot */
+ wait_queue_head_t boot_wait;
+ bool boot_complete;
+
+ /* IPC messaging */
+ struct sst_generic_ipc ipc;
+};
+
+struct skl_ipc_init_instance_msg {
+ u32 module_id;
+ u32 instance_id;
+ u16 param_data_size;
+ u8 ppl_instance_id;
+ u8 core_id;
+};
+
+struct skl_ipc_bind_unbind_msg {
+ u32 module_id;
+ u32 instance_id;
+ u32 dst_module_id;
+ u32 dst_instance_id;
+ u8 src_queue;
+ u8 dst_queue;
+ bool bind;
+};
+
+struct skl_ipc_large_config_msg {
+ u32 module_id;
+ u32 instance_id;
+ u32 large_param_id;
+ u32 param_data_size;
+};
+
+#define SKL_IPC_BOOT_MSECS 3000
+
+#define SKL_IPC_D3_MASK 0
+#define SKL_IPC_D0_MASK 3
+
+irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context);
+
+int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc,
+ u16 ppl_mem_size, u8 ppl_type, u8 instance_id);
+
+int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id);
+
+int skl_ipc_set_pipeline_state(struct sst_generic_ipc *sst_ipc,
+ u8 instance_id, enum skl_ipc_pipeline_state state);
+
+int skl_ipc_save_pipeline(struct sst_generic_ipc *ipc,
+ u8 instance_id, int dma_id);
+
+int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id);
+
+int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc,
+ struct skl_ipc_init_instance_msg *msg, void *param_data);
+
+int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc,
+ struct skl_ipc_bind_unbind_msg *msg);
+
+int skl_ipc_set_dx(struct sst_generic_ipc *ipc,
+ u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx);
+
+int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
+ struct skl_ipc_large_config_msg *msg, u32 *param);
+
+void skl_ipc_int_enable(struct sst_dsp *dsp);
+void skl_ipc_op_int_enable(struct sst_dsp *ctx);
+void skl_ipc_op_int_disable(struct sst_dsp *ctx);
+void skl_ipc_int_disable(struct sst_dsp *dsp);
+
+bool skl_ipc_int_status(struct sst_dsp *dsp);
+void skl_ipc_free(struct sst_generic_ipc *ipc);
+int skl_ipc_init(struct device *dev, struct skl_sst *skl);
+
+#endif /* __SKL_IPC_H */
diff --git a/kernel/sound/soc/intel/skylake/skl-sst.c b/kernel/sound/soc/intel/skylake/skl-sst.c
new file mode 100644
index 000000000..3b83dc99f
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-sst.c
@@ -0,0 +1,284 @@
+/*
+ * skl-sst.c - HDA DSP library functions for SKL platform
+ *
+ * Copyright (C) 2014-15, Intel Corporation.
+ * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
+ * Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-ipc.h"
+#include "skl-sst-ipc.h"
+
+#define SKL_BASEFW_TIMEOUT 300
+#define SKL_INIT_TIMEOUT 1000
+
+/* Intel HD Audio SRAM Window 0*/
+#define SKL_ADSP_SRAM0_BASE 0x8000
+
+/* Firmware status window */
+#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE
+#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4)
+
+#define SKL_INSTANCE_ID 0
+#define SKL_BASE_FW_MODULE_ID 0
+
+static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
+{
+ u32 cur_sts;
+
+ cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK;
+
+ return (cur_sts == status);
+}
+
+static int skl_transfer_firmware(struct sst_dsp *ctx,
+ const void *basefw, u32 base_fw_size)
+{
+ int ret = 0;
+
+ ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size);
+ if (ret < 0)
+ return ret;
+
+ ret = sst_dsp_register_poll(ctx,
+ SKL_ADSP_FW_STATUS,
+ SKL_FW_STS_MASK,
+ SKL_FW_RFW_START,
+ SKL_BASEFW_TIMEOUT,
+ "Firmware boot");
+
+ ctx->cl_dev.ops.cl_stop_dma(ctx);
+
+ return ret;
+}
+
+static int skl_load_base_firmware(struct sst_dsp *ctx)
+{
+ int ret = 0, i;
+ struct skl_sst *skl = ctx->thread_context;
+ u32 reg;
+
+ skl->boot_complete = false;
+ init_waitqueue_head(&skl->boot_wait);
+
+ if (ctx->fw == NULL) {
+ ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+ skl_dsp_disable_core(ctx);
+ return -EIO;
+ }
+ }
+
+ ret = skl_dsp_boot(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+ goto skl_load_base_firmware_failed;
+ }
+
+ ret = skl_cldma_prepare(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
+ goto skl_load_base_firmware_failed;
+ }
+
+ /* enable Interrupt */
+ skl_ipc_int_enable(ctx);
+ skl_ipc_op_int_enable(ctx);
+
+ /* check ROM Status */
+ for (i = SKL_INIT_TIMEOUT; i > 0; --i) {
+ if (skl_check_fw_status(ctx, SKL_FW_INIT)) {
+ dev_dbg(ctx->dev,
+ "ROM loaded, we can continue with FW loading\n");
+ break;
+ }
+ mdelay(1);
+ }
+ if (!i) {
+ reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS);
+ dev_err(ctx->dev,
+ "Timeout waiting for ROM init done, reg:0x%x\n", reg);
+ ret = -EIO;
+ goto skl_load_base_firmware_failed;
+ }
+
+ ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
+ goto skl_load_base_firmware_failed;
+ } else {
+ ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
+ msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+ if (ret == 0) {
+ dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n");
+ ret = -EIO;
+ goto skl_load_base_firmware_failed;
+ }
+
+ dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
+ skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ }
+ return 0;
+
+skl_load_base_firmware_failed:
+ skl_dsp_disable_core(ctx);
+ release_firmware(ctx->fw);
+ ctx->fw = NULL;
+ return ret;
+}
+
+static int skl_set_dsp_D0(struct sst_dsp *ctx)
+{
+ int ret;
+
+ ret = skl_load_base_firmware(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "unable to load firmware\n");
+ return ret;
+ }
+
+ skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+
+ return ret;
+}
+
+static int skl_set_dsp_D3(struct sst_dsp *ctx)
+{
+ int ret;
+ struct skl_ipc_dxstate_info dx;
+ struct skl_sst *skl = ctx->thread_context;
+
+ dev_dbg(ctx->dev, "In %s:\n", __func__);
+ mutex_lock(&ctx->mutex);
+ if (!is_skl_dsp_running(ctx)) {
+ mutex_unlock(&ctx->mutex);
+ return 0;
+ }
+ mutex_unlock(&ctx->mutex);
+
+ dx.core_mask = SKL_DSP_CORE0_MASK;
+ dx.dx_mask = SKL_IPC_D3_MASK;
+ ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set DSP to D3 state\n");
+ return ret;
+ }
+
+ ret = skl_dsp_disable_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret);
+ ret = -EIO;
+ }
+ skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+
+ /* disable Interrupt */
+ ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+ skl_cldma_int_disable(ctx);
+ skl_ipc_op_int_disable(ctx);
+ skl_ipc_int_disable(ctx);
+
+ return ret;
+}
+
+static unsigned int skl_get_errorcode(struct sst_dsp *ctx)
+{
+ return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE);
+}
+
+static struct skl_dsp_fw_ops skl_fw_ops = {
+ .set_state_D0 = skl_set_dsp_D0,
+ .set_state_D3 = skl_set_dsp_D3,
+ .load_fw = skl_load_base_firmware,
+ .get_fw_errcode = skl_get_errorcode,
+};
+
+static struct sst_ops skl_ops = {
+ .irq_handler = skl_dsp_sst_interrupt,
+ .write = sst_shim32_write,
+ .read = sst_shim32_read,
+ .ram_read = sst_memcpy_fromio_32,
+ .ram_write = sst_memcpy_toio_32,
+ .free = skl_dsp_free,
+};
+
+static struct sst_dsp_device skl_dev = {
+ .thread = skl_dsp_irq_thread_handler,
+ .ops = &skl_ops,
+};
+
+int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+ struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp)
+{
+ struct skl_sst *skl;
+ struct sst_dsp *sst;
+ int ret;
+
+ skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
+ if (skl == NULL)
+ return -ENOMEM;
+
+ skl->dev = dev;
+ skl_dev.thread_context = skl;
+
+ skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
+ if (!skl->dsp) {
+ dev_err(skl->dev, "%s: no device\n", __func__);
+ return -ENODEV;
+ }
+
+ sst = skl->dsp;
+
+ sst->addr.lpe = mmio_base;
+ sst->addr.shim = mmio_base;
+ sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
+ SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
+
+ sst->dsp_ops = dsp_ops;
+ sst->fw_ops = skl_fw_ops;
+
+ ret = skl_ipc_init(dev, skl);
+ if (ret)
+ return ret;
+
+ ret = sst->fw_ops.load_fw(sst);
+ if (ret < 0) {
+ dev_err(dev, "Load base fw failed : %d", ret);
+ return ret;
+ }
+
+ if (dsp)
+ *dsp = skl;
+
+ return 0;
+
+ skl_ipc_free(&skl->ipc);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+
+void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
+{
+ skl_ipc_free(&ctx->ipc);
+ ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
+ ctx->dsp->ops->free(ctx->dsp);
+}
+EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Skylake IPC driver");
diff --git a/kernel/sound/soc/intel/skylake/skl-topology.c b/kernel/sound/soc/intel/skylake/skl-topology.c
new file mode 100644
index 000000000..ad4d0f826
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-topology.c
@@ -0,0 +1,1254 @@
+/*
+ * skl-topology.c - Implements Platform component ALSA controls/widget
+ * handlers.
+ *
+ * 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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-topology.h>
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+#include "skl-topology.h"
+#include "skl.h"
+#include "skl-tplg-interface.h"
+
+#define SKL_CH_FIXUP_MASK (1 << 0)
+#define SKL_RATE_FIXUP_MASK (1 << 1)
+#define SKL_FMT_FIXUP_MASK (1 << 2)
+
+/*
+ * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
+ * ignore. This helpers checks if the SKL driver handles this widget type
+ */
+static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai_out:
+ case snd_soc_dapm_switch:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/*
+ * Each pipelines needs memory to be allocated. Check if we have free memory
+ * from available pool. Then only add this to pool
+ * This is freed when pipe is deleted
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+
+ if (skl->resource.mem + mconfig->pipe->memory_pages >
+ skl->resource.max_mem) {
+ dev_err(ctx->dev,
+ "%s: module_id %d instance %d\n", __func__,
+ mconfig->id.module_id,
+ mconfig->id.instance_id);
+ dev_err(ctx->dev,
+ "exceeds ppl memory available %d mem %d\n",
+ skl->resource.max_mem, skl->resource.mem);
+ return false;
+ }
+
+ skl->resource.mem += mconfig->pipe->memory_pages;
+ return true;
+}
+
+/*
+ * Pipeline needs needs DSP CPU resources for computation, this is
+ * quantified in MCPS (Million Clocks Per Second) required for module/pipe
+ *
+ * Each pipelines needs mcps to be allocated. Check if we have mcps for this
+ * pipe. This adds the mcps to driver counter
+ * This is removed on pipeline delete
+ */
+static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+
+ if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
+ dev_err(ctx->dev,
+ "%s: module_id %d instance %d\n", __func__,
+ mconfig->id.module_id, mconfig->id.instance_id);
+ dev_err(ctx->dev,
+ "exceeds ppl memory available %d > mem %d\n",
+ skl->resource.max_mcps, skl->resource.mcps);
+ return false;
+ }
+
+ skl->resource.mcps += mconfig->mcps;
+ return true;
+}
+
+/*
+ * Free the mcps when tearing down
+ */
+static void
+skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ skl->resource.mcps -= mconfig->mcps;
+}
+
+/*
+ * Free the memory when tearing down
+ */
+static void
+skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ skl->resource.mem -= mconfig->pipe->memory_pages;
+}
+
+
+static void skl_dump_mconfig(struct skl_sst *ctx,
+ struct skl_module_cfg *mcfg)
+{
+ dev_dbg(ctx->dev, "Dumping config\n");
+ dev_dbg(ctx->dev, "Input Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
+ dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
+ dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n",
+ mcfg->in_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "Output Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
+ dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n",
+ mcfg->out_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
+}
+
+static void skl_tplg_update_params(struct skl_module_fmt *fmt,
+ struct skl_pipe_params *params, int fixup)
+{
+ if (fixup & SKL_RATE_FIXUP_MASK)
+ fmt->s_freq = params->s_freq;
+ if (fixup & SKL_CH_FIXUP_MASK)
+ fmt->channels = params->ch;
+ if (fixup & SKL_FMT_FIXUP_MASK)
+ fmt->valid_bit_depth = params->s_fmt;
+}
+
+/*
+ * A pipeline may have modules which impact the pcm parameters, like SRC,
+ * channel converter, format converter.
+ * We need to calculate the output params by applying the 'fixup'
+ * Topology will tell driver which type of fixup is to be applied by
+ * supplying the fixup mask, so based on that we calculate the output
+ *
+ * Now In FE the pcm hw_params is source/target format. Same is applicable
+ * for BE with its hw_params invoked.
+ * here based on FE, BE pipeline and direction we calculate the input and
+ * outfix and then apply that for a module
+ */
+static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
+ struct skl_pipe_params *params, bool is_fe)
+{
+ int in_fixup, out_fixup;
+ struct skl_module_fmt *in_fmt, *out_fmt;
+
+ in_fmt = &m_cfg->in_fmt;
+ out_fmt = &m_cfg->out_fmt;
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (is_fe) {
+ in_fixup = m_cfg->params_fixup;
+ out_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ } else {
+ out_fixup = m_cfg->params_fixup;
+ in_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ }
+ } else {
+ if (is_fe) {
+ out_fixup = m_cfg->params_fixup;
+ in_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ } else {
+ in_fixup = m_cfg->params_fixup;
+ out_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ }
+ }
+
+ skl_tplg_update_params(in_fmt, params, in_fixup);
+ skl_tplg_update_params(out_fmt, params, out_fixup);
+}
+
+/*
+ * A module needs input and output buffers, which are dependent upon pcm
+ * params, so once we have calculate params, we need buffer calculation as
+ * well.
+ */
+static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
+ struct skl_module_cfg *mcfg)
+{
+ int multiplier = 1;
+
+ if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
+ multiplier = 5;
+
+ mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
+ (mcfg->in_fmt.channels) *
+ (mcfg->in_fmt.bit_depth >> 3) *
+ multiplier;
+
+ mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
+ (mcfg->out_fmt.channels) *
+ (mcfg->out_fmt.bit_depth >> 3) *
+ multiplier;
+}
+
+static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
+ struct skl_sst *ctx)
+{
+ struct skl_module_cfg *m_cfg = w->priv;
+ struct skl_pipe_params *params = m_cfg->pipe->p_params;
+ int p_conn_type = m_cfg->pipe->conn_type;
+ bool is_fe;
+
+ if (!m_cfg->params_fixup)
+ return;
+
+ dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n",
+ w->name);
+
+ skl_dump_mconfig(ctx, m_cfg);
+
+ if (p_conn_type == SKL_PIPE_CONN_TYPE_FE)
+ is_fe = true;
+ else
+ is_fe = false;
+
+ skl_tplg_update_params_fixup(m_cfg, params, is_fe);
+ skl_tplg_update_buffer_size(ctx, m_cfg);
+
+ dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n",
+ w->name);
+
+ skl_dump_mconfig(ctx, m_cfg);
+}
+
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
+ * to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_alloc_pipe_widget(struct device *dev,
+ struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
+{
+ struct skl_module_cfg *src_module = NULL;
+ struct snd_soc_dapm_path *p = NULL;
+ struct skl_pipe_module *p_module = NULL;
+
+ p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
+ if (!p_module)
+ return -ENOMEM;
+
+ p_module->w = w;
+ list_add_tail(&p_module->node, &pipe->w_list);
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if ((p->sink->priv == NULL)
+ && (!is_skl_dsp_widget_type(w)))
+ continue;
+
+ if ((p->sink->priv != NULL) && p->connect
+ && is_skl_dsp_widget_type(p->sink)) {
+
+ src_module = p->sink->priv;
+ if (pipe->ppl_id == src_module->pipe->ppl_id)
+ skl_tplg_alloc_pipe_widget(dev,
+ p->sink, pipe);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Inside a pipe instance, we can have various modules. These modules need
+ * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
+ * skl_init_module() routine, so invoke that for all modules in a pipeline
+ */
+static int
+skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
+{
+ struct skl_pipe_module *w_module;
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ list_for_each_entry(w_module, &pipe->w_list, node) {
+ w = w_module->w;
+ mconfig = w->priv;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * apply fix/conversion to module params based on
+ * FE/BE params
+ */
+ skl_tplg_update_module_params(w, ctx);
+ ret = skl_init_module(ctx, mconfig, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
+ * need create the pipeline. So we do following:
+ * - check the resources
+ * - Create the pipeline
+ * - Initialize the modules in pipeline
+ * - finally bind all modules together
+ */
+static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret;
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -EBUSY;
+
+ if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * Create a list of modules for pipe.
+ * This list contains modules from source to sink
+ */
+ ret = skl_create_pipeline(ctx, mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * we create a w_list of all widgets in that pipe. This list is not
+ * freed on PMD event as widgets within a pipe are static. This
+ * saves us cycles to get widgets in pipe every time.
+ *
+ * So if we have already initialized all the widgets of a pipeline
+ * we skip, so check for list_empty and create the list if empty
+ */
+ if (list_empty(&s_pipe->w_list)) {
+ ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Init all pipe modules from source to sink */
+ ret = skl_tplg_init_pipe_modules(skl, s_pipe);
+ if (ret < 0)
+ return ret;
+
+ /* Bind modules from source to sink */
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_bind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ return 0;
+}
+
+/*
+ * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
+ * we need to do following:
+ * - Bind to sink pipeline
+ * Since the sink pipes can be running and we don't get mixer event on
+ * connect for already running mixer, we need to find the sink pipes
+ * here and bind to them. This way dynamic connect works.
+ * - Start sink pipeline, if not running
+ * - Then run current pipe
+ */
+static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_path *p;
+ struct skl_dapm_path_list *path_list;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ /*
+ * find which sink it is connected to, bind with the sink,
+ * if sink is not started, start sink pipe first, then start
+ * this pipe
+ */
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
+ dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->sink->priv != NULL) &&
+ is_skl_dsp_widget_type(p->sink)) {
+
+ sink = p->sink;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+
+ /* Bind source to sink, mixin is always source */
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ /* Start sinks pipe first */
+ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+ }
+
+ path_list = kzalloc(
+ sizeof(struct skl_dapm_path_list),
+ GFP_KERNEL);
+ if (path_list == NULL)
+ return -ENOMEM;
+
+ /* Add connected path to one global list */
+ path_list->dapm_path = p;
+ list_add_tail(&path_list->node, &skl->dapm_path_list);
+ break;
+ }
+ }
+
+ /* Start source pipe last after starting all sinks */
+ ret = skl_run_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * in the Post-PMU event of mixer we need to do following:
+ * - Check if this pipe is running
+ * - if not, then
+ * - bind this pipeline to its source pipeline
+ * if source pipe is already running, this means it is a dynamic
+ * connection and we need to bind only to that pipe
+ * - start this pipeline
+ */
+static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret = 0;
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int src_pipe_started = 0;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /*
+ * If source pipe is already started, that means source is driving
+ * one more sink before this sink got connected, Since source is
+ * started, bind this sink to source and start this pipe.
+ */
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
+ dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->source->priv != NULL) &&
+ is_skl_dsp_widget_type(p->source)) {
+ source = p->source;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+ src_pipe_started = 1;
+
+ /*
+ * check pipe state, then no need to bind or start
+ * the pipe
+ */
+ if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
+ src_pipe_started = 0;
+ }
+ }
+
+ if (src_pipe_started) {
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Pre-PMD event of mixer we need to do following:
+ * - Stop the pipe
+ * - find the source connections and remove that from dapm_path_list
+ * - unbind with source pipelines if still connected
+ */
+static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /* Stop the pipe */
+ ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ /*
+ * This list, dapm_path_list handling here does not need any locks
+ * as we are under dapm lock while handling widget events.
+ * List can be manipulated safely only under dapm widgets handler
+ * routines
+ */
+ list_for_each_entry_safe(path_list, tmp_list,
+ &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->sink == sink) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ source = path_list->dapm_path->source;
+ src_mconfig = source->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * If path_found == 1, that means pmd for source pipe has
+ * not occurred, source is connected to some other sink.
+ * so its responsibility of sink to unbind itself from source.
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of mixer we need to do following:
+ * - Free the mcps used
+ * - Free the mem used
+ * - Unbind the modules within the pipeline
+ * - Delete the pipeline (modules are not required to be explicitly
+ * deleted, pipeline delete is enough here
+ */
+static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ int ret = 0;
+
+ skl_tplg_free_pipe_mcps(skl, mconfig);
+
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_unbind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ ret = skl_delete_pipe(ctx, mconfig->pipe);
+ skl_tplg_free_pipe_mem(skl, mconfig);
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of PGA we need to do following:
+ * - Free the mcps used
+ * - Stop the pipeline
+ * - In source pipe is connected, unbind with source pipelines
+ */
+static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_path_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ skl_tplg_free_pipe_mcps(skl, src_mconfig);
+ /* Stop the pipe since this is a mixin module */
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->source == source) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ sink = path_list->dapm_path->sink;
+ sink_mconfig = sink->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * This is a connector and if path is found that means
+ * unbind between source and sink has not happened yet
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If
+ * mixer is not required then it is treated as static mixer aka vmixer with
+ * a hard path to source module
+ * So we don't need to check if source is started or not as hard path puts
+ * dependency on each other
+ */
+static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
+ * second one is required that is created as another pipe entity.
+ * The mixer is responsible for pipe management and represent a pipeline
+ * instance
+ */
+static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMU:
+ return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_PRE_PMD:
+ return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assumed rest of the modules in pipeline are PGA. But we
+ * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
+ * the sink when it is running (two FE to one BE or one FE to two BE)
+ * scenarios
+ */
+static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_pga_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * The FE params are passed by hw_params of the DAI.
+ * On hw_params, the params are stored in Gateway module of the FE and we
+ * need to calculate the format in DSP module configuration, that
+ * conversion is done here
+ */
+int skl_tplg_update_pipe_params(struct device *dev,
+ struct skl_module_cfg *mconfig,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mconfig->pipe;
+ struct skl_module_fmt *format = NULL;
+
+ memcpy(pipe->p_params, params, sizeof(*params));
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ format = &mconfig->in_fmt;
+ else
+ format = &mconfig->out_fmt;
+
+ /* set the hw_params */
+ format->s_freq = params->s_freq;
+ format->channels = params->ch;
+ format->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
+
+ /*
+ * 16 bit is 16 bit container whereas 24 bit is in 32 bit
+ * container so update bit depth accordingly
+ */
+ switch (format->valid_bit_depth) {
+ case SKL_DEPTH_16BIT:
+ format->bit_depth = format->valid_bit_depth;
+ break;
+
+ case SKL_DEPTH_24BIT:
+ format->bit_depth = SKL_DEPTH_32BIT;
+ break;
+
+ default:
+ dev_err(dev, "Invalid bit depth %x for pipe\n",
+ format->valid_bit_depth);
+ return -EINVAL;
+ }
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mconfig->ibs = (format->s_freq / 1000) *
+ (format->channels) *
+ (format->bit_depth >> 3);
+ } else {
+ mconfig->obs = (format->s_freq / 1000) *
+ (format->channels) *
+ (format->bit_depth >> 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Query the module config for the FE DAI
+ * This is used to find the hw_params set for that DAI and apply to FE
+ * pipeline
+ */
+struct skl_module_cfg *
+skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p = NULL;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connect && p->sink->power &&
+ is_skl_dsp_widget_type(p->sink))
+ continue;
+
+ if (p->sink->priv) {
+ dev_dbg(dai->dev, "set params for %s\n",
+ p->sink->name);
+ return p->sink->priv;
+ }
+ }
+ } else {
+ w = dai->capture_widget;
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connect && p->source->power &&
+ is_skl_dsp_widget_type(p->source))
+ continue;
+
+ if (p->source->priv) {
+ dev_dbg(dai->dev, "set params for %s\n",
+ p->source->name);
+ return p->source->priv;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static u8 skl_tplg_be_link_type(int dev_type)
+{
+ int ret;
+
+ switch (dev_type) {
+ case SKL_DEVICE_BT:
+ ret = NHLT_LINK_SSP;
+ break;
+
+ case SKL_DEVICE_DMIC:
+ ret = NHLT_LINK_DMIC;
+ break;
+
+ case SKL_DEVICE_I2S:
+ ret = NHLT_LINK_SSP;
+ break;
+
+ case SKL_DEVICE_HDALINK:
+ ret = NHLT_LINK_HDA;
+ break;
+
+ default:
+ ret = NHLT_LINK_INVALID;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Fill the BE gateway parameters
+ * The BE gateway expects a blob of parameters which are kept in the ACPI
+ * NHLT blob, so query the blob for interface type (i2s/pdm) and instance.
+ * The port can have multiple settings so pick based on the PCM
+ * parameters
+ */
+static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
+ struct skl_module_cfg *mconfig,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mconfig->pipe;
+ struct nhlt_specific_cfg *cfg;
+ struct skl *skl = get_skl_ctx(dai->dev);
+ int link_type = skl_tplg_be_link_type(mconfig->dev_type);
+
+ memcpy(pipe->p_params, params, sizeof(*params));
+
+ /* update the blob based on virtual bus_id*/
+ cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
+ params->s_fmt, params->ch,
+ params->s_freq, params->stream);
+ if (cfg) {
+ mconfig->formats_config.caps_size = cfg->size;
+ mconfig->formats_config.caps = (u32 *) &cfg->caps;
+ } else {
+ dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n",
+ mconfig->vbus_id, link_type,
+ params->stream);
+ dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n",
+ params->ch, params->s_freq, params->s_fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
+ struct snd_soc_dapm_widget *w,
+ struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_path *p;
+ int ret = -EIO;
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connect && is_skl_dsp_widget_type(p->source) &&
+ p->source->priv) {
+
+ if (!p->source->power) {
+ ret = skl_tplg_be_fill_pipe_params(
+ dai, p->source->priv,
+ params);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EBUSY;
+ }
+ } else {
+ ret = skl_tplg_be_set_src_pipe_params(
+ dai, p->source, params);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
+ struct snd_soc_dapm_widget *w, struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_path *p = NULL;
+ int ret = -EIO;
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connect && is_skl_dsp_widget_type(p->sink) &&
+ p->sink->priv) {
+
+ if (!p->sink->power) {
+ ret = skl_tplg_be_fill_pipe_params(
+ dai, p->sink->priv, params);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EBUSY;
+ }
+
+ } else {
+ ret = skl_tplg_be_set_sink_pipe_params(
+ dai, p->sink, params);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * BE hw_params can be a source parameters (capture) or sink parameters
+ * (playback). Based on sink and source we need to either find the source
+ * list or the sink list and set the pipeline parameters
+ */
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_widget *w;
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+
+ return skl_tplg_be_set_src_pipe_params(dai, w, params);
+
+ } else {
+ w = dai->capture_widget;
+
+ return skl_tplg_be_set_sink_pipe_params(dai, w, params);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
+ {SKL_MIXER_EVENT, skl_tplg_mixer_event},
+ {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+ {SKL_PGA_EVENT, skl_tplg_pga_event},
+};
+
+/*
+ * The topology binary passes the pin info for a module so initialize the pin
+ * info passed into module instance
+ */
+static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
+ struct skl_module_pin *m_pin,
+ bool is_dynamic, int max_pin)
+{
+ int i;
+
+ for (i = 0; i < max_pin; i++) {
+ m_pin[i].id.module_id = dfw_pin[i].module_id;
+ m_pin[i].id.instance_id = dfw_pin[i].instance_id;
+ m_pin[i].in_use = false;
+ m_pin[i].is_dynamic = is_dynamic;
+ }
+}
+
+/*
+ * Add pipeline from topology binary into driver pipeline list
+ *
+ * If already added we return that instance
+ * Otherwise we create a new instance and add into driver list
+ */
+static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
+ struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+{
+ struct skl_pipeline *ppl;
+ struct skl_pipe *pipe;
+ struct skl_pipe_params *params;
+
+ list_for_each_entry(ppl, &skl->ppl_list, node) {
+ if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
+ return ppl->pipe;
+ }
+
+ ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
+ if (!ppl)
+ return NULL;
+
+ pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
+ if (!pipe)
+ return NULL;
+
+ params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return NULL;
+
+ pipe->ppl_id = dfw_pipe->pipe_id;
+ pipe->memory_pages = dfw_pipe->memory_pages;
+ pipe->pipe_priority = dfw_pipe->pipe_priority;
+ pipe->conn_type = dfw_pipe->conn_type;
+ pipe->state = SKL_PIPE_INVALID;
+ pipe->p_params = params;
+ INIT_LIST_HEAD(&pipe->w_list);
+
+ ppl->pipe = pipe;
+ list_add(&ppl->node, &skl->ppl_list);
+
+ return ppl->pipe;
+}
+
+/*
+ * Topology core widget load callback
+ *
+ * This is used to save the private data for each widget which gives
+ * information to the driver about module and pipeline parameters which DSP
+ * FW expects like ids, resource values, formats etc
+ */
+static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ int ret;
+ struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl_module_cfg *mconfig;
+ struct skl_pipe *pipe;
+ struct skl_dfw_module *dfw_config =
+ (struct skl_dfw_module *)tplg_w->priv.data;
+
+ if (!tplg_w->priv.size)
+ goto bind_event;
+
+ mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
+
+ if (!mconfig)
+ return -ENOMEM;
+
+ w->priv = mconfig;
+ mconfig->id.module_id = dfw_config->module_id;
+ mconfig->id.instance_id = dfw_config->instance_id;
+ mconfig->mcps = dfw_config->max_mcps;
+ mconfig->ibs = dfw_config->ibs;
+ mconfig->obs = dfw_config->obs;
+ mconfig->core_id = dfw_config->core_id;
+ mconfig->max_in_queue = dfw_config->max_in_queue;
+ mconfig->max_out_queue = dfw_config->max_out_queue;
+ mconfig->is_loadable = dfw_config->is_loadable;
+ mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
+ mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
+ mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
+ mconfig->in_fmt.valid_bit_depth =
+ dfw_config->in_fmt.valid_bit_depth;
+ mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
+ mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
+ mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
+ mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
+ mconfig->out_fmt.valid_bit_depth =
+ dfw_config->out_fmt.valid_bit_depth;
+ mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
+ mconfig->params_fixup = dfw_config->params_fixup;
+ mconfig->converter = dfw_config->converter;
+ mconfig->m_type = dfw_config->module_type;
+ mconfig->vbus_id = dfw_config->vbus_id;
+
+ pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
+ if (pipe)
+ mconfig->pipe = pipe;
+
+ mconfig->dev_type = dfw_config->dev_type;
+ mconfig->hw_conn_type = dfw_config->hw_conn_type;
+ mconfig->time_slot = dfw_config->time_slot;
+ mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
+
+ mconfig->m_in_pin = devm_kzalloc(bus->dev,
+ (mconfig->max_in_queue) *
+ sizeof(*mconfig->m_in_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_in_pin)
+ return -ENOMEM;
+
+ mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
+ sizeof(*mconfig->m_out_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_out_pin)
+ return -ENOMEM;
+
+ skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
+ dfw_config->is_dynamic_in_pin,
+ mconfig->max_in_queue);
+
+ skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
+ dfw_config->is_dynamic_out_pin,
+ mconfig->max_out_queue);
+
+
+ if (mconfig->formats_config.caps_size == 0)
+ goto bind_event;
+
+ mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
+ mconfig->formats_config.caps_size, GFP_KERNEL);
+
+ if (mconfig->formats_config.caps == NULL)
+ return -ENOMEM;
+
+ memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
+ dfw_config->caps.caps_size);
+
+bind_event:
+ if (tplg_w->event_type == 0) {
+ dev_dbg(bus->dev, "ASoC: No event handler required\n");
+ return 0;
+ }
+
+ ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
+ ARRAY_SIZE(skl_tplg_widget_ops),
+ tplg_w->event_type);
+
+ if (ret) {
+ dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
+ __func__, tplg_w->event_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_tplg_ops skl_tplg_ops = {
+ .widget_load = skl_tplg_widget_load,
+};
+
+/* This will be read from topology manifest, currently defined here */
+#define SKL_MAX_MCPS 30000000
+#define SKL_FW_MAX_MEM 1000000
+
+/*
+ * SKL topology init routine
+ */
+int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
+{
+ int ret;
+ const struct firmware *fw;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+ if (ret < 0) {
+ dev_err(bus->dev, "tplg fw %s load failed with %d\n",
+ "dfw_sst.bin", ret);
+ return ret;
+ }
+
+ /*
+ * The complete tplg for SKL is loaded as index 0, we don't use
+ * any other index
+ */
+ ret = snd_soc_tplg_component_load(&platform->component,
+ &skl_tplg_ops, fw, 0);
+ if (ret < 0) {
+ dev_err(bus->dev, "tplg component load failed%d\n", ret);
+ return -EINVAL;
+ }
+
+ skl->resource.max_mcps = SKL_MAX_MCPS;
+ skl->resource.max_mem = SKL_FW_MAX_MEM;
+
+ skl->tplg = fw;
+
+ return 0;
+}
diff --git a/kernel/sound/soc/intel/skylake/skl-topology.h b/kernel/sound/soc/intel/skylake/skl-topology.h
new file mode 100644
index 000000000..76053a8de
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-topology.h
@@ -0,0 +1,318 @@
+/*
+ * skl_topology.h - Intel HDA Platform topology header file
+ *
+ * Copyright (C) 2014-15 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef __SKL_TOPOLOGY_H__
+#define __SKL_TOPOLOGY_H__
+
+#include <linux/types.h>
+
+#include <sound/hdaudio_ext.h>
+#include <sound/soc.h>
+#include "skl.h"
+#include "skl-tplg-interface.h"
+
+#define BITS_PER_BYTE 8
+#define MAX_TS_GROUPS 8
+#define MAX_DMIC_TS_GROUPS 4
+#define MAX_FIXED_DMIC_PARAMS_SIZE 727
+
+/* Maximum number of coefficients up down mixer module */
+#define UP_DOWN_MIXER_MAX_COEFF 6
+
+enum skl_channel_index {
+ SKL_CHANNEL_LEFT = 0,
+ SKL_CHANNEL_RIGHT = 1,
+ SKL_CHANNEL_CENTER = 2,
+ SKL_CHANNEL_LEFT_SURROUND = 3,
+ SKL_CHANNEL_CENTER_SURROUND = 3,
+ SKL_CHANNEL_RIGHT_SURROUND = 4,
+ SKL_CHANNEL_LFE = 7,
+ SKL_CHANNEL_INVALID = 0xF,
+};
+
+enum skl_bitdepth {
+ SKL_DEPTH_8BIT = 8,
+ SKL_DEPTH_16BIT = 16,
+ SKL_DEPTH_24BIT = 24,
+ SKL_DEPTH_32BIT = 32,
+ SKL_DEPTH_INVALID
+};
+
+enum skl_interleaving {
+ /* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */
+ SKL_INTERLEAVING_PER_CHANNEL = 0,
+ /* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */
+ SKL_INTERLEAVING_PER_SAMPLE = 1,
+};
+
+enum skl_s_freq {
+ SKL_FS_8000 = 8000,
+ SKL_FS_11025 = 11025,
+ SKL_FS_12000 = 12000,
+ SKL_FS_16000 = 16000,
+ SKL_FS_22050 = 22050,
+ SKL_FS_24000 = 24000,
+ SKL_FS_32000 = 32000,
+ SKL_FS_44100 = 44100,
+ SKL_FS_48000 = 48000,
+ SKL_FS_64000 = 64000,
+ SKL_FS_88200 = 88200,
+ SKL_FS_96000 = 96000,
+ SKL_FS_128000 = 128000,
+ SKL_FS_176400 = 176400,
+ SKL_FS_192000 = 192000,
+ SKL_FS_INVALID
+};
+
+enum skl_widget_type {
+ SKL_WIDGET_VMIXER = 1,
+ SKL_WIDGET_MIXER = 2,
+ SKL_WIDGET_PGA = 3,
+ SKL_WIDGET_MUX = 4
+};
+
+struct skl_audio_data_format {
+ enum skl_s_freq s_freq;
+ enum skl_bitdepth bit_depth;
+ u32 channel_map;
+ enum skl_ch_cfg ch_cfg;
+ enum skl_interleaving interleaving;
+ u8 number_of_channels;
+ u8 valid_bit_depth;
+ u8 sample_type;
+ u8 reserved[1];
+} __packed;
+
+struct skl_base_cfg {
+ u32 cps;
+ u32 ibs;
+ u32 obs;
+ u32 is_pages;
+ struct skl_audio_data_format audio_fmt;
+};
+
+struct skl_cpr_gtw_cfg {
+ u32 node_id;
+ u32 dma_buffer_size;
+ u32 config_length;
+ /* not mandatory; required only for DMIC/I2S */
+ u32 config_data[1];
+} __packed;
+
+struct skl_cpr_cfg {
+ struct skl_base_cfg base_cfg;
+ struct skl_audio_data_format out_fmt;
+ u32 cpr_feature_mask;
+ struct skl_cpr_gtw_cfg gtw_cfg;
+} __packed;
+
+
+struct skl_src_module_cfg {
+ struct skl_base_cfg base_cfg;
+ enum skl_s_freq src_cfg;
+} __packed;
+
+struct notification_mask {
+ u32 notify;
+ u32 enable;
+} __packed;
+
+struct skl_up_down_mixer_cfg {
+ struct skl_base_cfg base_cfg;
+ enum skl_ch_cfg out_ch_cfg;
+ /* This should be set to 1 if user coefficients are required */
+ u32 coeff_sel;
+ /* Pass the user coeff in this array */
+ s32 coeff[UP_DOWN_MIXER_MAX_COEFF];
+} __packed;
+
+enum skl_dma_type {
+ SKL_DMA_HDA_HOST_OUTPUT_CLASS = 0,
+ SKL_DMA_HDA_HOST_INPUT_CLASS = 1,
+ SKL_DMA_HDA_HOST_INOUT_CLASS = 2,
+ SKL_DMA_HDA_LINK_OUTPUT_CLASS = 8,
+ SKL_DMA_HDA_LINK_INPUT_CLASS = 9,
+ SKL_DMA_HDA_LINK_INOUT_CLASS = 0xA,
+ SKL_DMA_DMIC_LINK_INPUT_CLASS = 0xB,
+ SKL_DMA_I2S_LINK_OUTPUT_CLASS = 0xC,
+ SKL_DMA_I2S_LINK_INPUT_CLASS = 0xD,
+};
+
+union skl_ssp_dma_node {
+ u8 val;
+ struct {
+ u8 time_slot_index:4;
+ u8 i2s_instance:4;
+ } dma_node;
+};
+
+union skl_connector_node_id {
+ u32 val;
+ struct {
+ u32 vindex:8;
+ u32 dma_type:4;
+ u32 rsvd:20;
+ } node;
+};
+
+struct skl_module_fmt {
+ u32 channels;
+ u32 s_freq;
+ u32 bit_depth;
+ u32 valid_bit_depth;
+ u32 ch_cfg;
+};
+
+struct skl_module_inst_id {
+ u32 module_id;
+ u32 instance_id;
+};
+
+struct skl_module_pin {
+ struct skl_module_inst_id id;
+ u8 pin_index;
+ bool is_dynamic;
+ bool in_use;
+};
+
+struct skl_specific_cfg {
+ u32 caps_size;
+ u32 *caps;
+};
+
+enum skl_pipe_state {
+ SKL_PIPE_INVALID = 0,
+ SKL_PIPE_CREATED = 1,
+ SKL_PIPE_PAUSED = 2,
+ SKL_PIPE_STARTED = 3
+};
+
+struct skl_pipe_module {
+ struct snd_soc_dapm_widget *w;
+ struct list_head node;
+};
+
+struct skl_pipe_params {
+ u8 host_dma_id;
+ u8 link_dma_id;
+ u32 ch;
+ u32 s_freq;
+ u32 s_fmt;
+ u8 linktype;
+ int stream;
+};
+
+struct skl_pipe {
+ u8 ppl_id;
+ u8 pipe_priority;
+ u16 conn_type;
+ u32 memory_pages;
+ struct skl_pipe_params *p_params;
+ enum skl_pipe_state state;
+ struct list_head w_list;
+};
+
+enum skl_module_state {
+ SKL_MODULE_UNINIT = 0,
+ SKL_MODULE_INIT_DONE = 1,
+ SKL_MODULE_LOADED = 2,
+ SKL_MODULE_UNLOADED = 3,
+ SKL_MODULE_BIND_DONE = 4
+};
+
+struct skl_module_cfg {
+ struct skl_module_inst_id id;
+ struct skl_module_fmt in_fmt;
+ struct skl_module_fmt out_fmt;
+ u8 max_in_queue;
+ u8 max_out_queue;
+ u8 in_queue_mask;
+ u8 out_queue_mask;
+ u8 in_queue;
+ u8 out_queue;
+ u32 mcps;
+ u32 ibs;
+ u32 obs;
+ u8 is_loadable;
+ u8 core_id;
+ u8 dev_type;
+ u8 dma_id;
+ u8 time_slot;
+ u32 params_fixup;
+ u32 converter;
+ u32 vbus_id;
+ struct skl_module_pin *m_in_pin;
+ struct skl_module_pin *m_out_pin;
+ enum skl_module_type m_type;
+ enum skl_hw_conn_type hw_conn_type;
+ enum skl_module_state m_state;
+ struct skl_pipe *pipe;
+ struct skl_specific_cfg formats_config;
+};
+
+struct skl_pipeline {
+ struct skl_pipe *pipe;
+ struct list_head node;
+};
+
+struct skl_dapm_path_list {
+ struct snd_soc_dapm_path *dapm_path;
+ struct list_head node;
+};
+
+static inline struct skl *get_skl_ctx(struct device *dev)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+
+ return ebus_to_skl(ebus);
+}
+
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params);
+void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params, int stream);
+int skl_tplg_init(struct snd_soc_platform *platform,
+ struct hdac_ext_bus *ebus);
+struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
+ struct snd_soc_dai *dai, int stream);
+int skl_tplg_update_pipe_params(struct device *dev,
+ struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+
+int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
+
+int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+
+int skl_pause_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+
+int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+
+int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+
+int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config,
+ char *param);
+
+int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
+ *src_module, struct skl_module_cfg *dst_module);
+
+int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg
+ *src_module, struct skl_module_cfg *dst_module);
+
+enum skl_bitdepth skl_get_bit_depth(int params);
+#endif
diff --git a/kernel/sound/soc/intel/skylake/skl-tplg-interface.h b/kernel/sound/soc/intel/skylake/skl-tplg-interface.h
new file mode 100644
index 000000000..2bc396d54
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl-tplg-interface.h
@@ -0,0 +1,171 @@
+/*
+ * skl-tplg-interface.h - Intel DSP FW private data interface
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * Nilofer, Samreen <samreen.nilofer@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __HDA_TPLG_INTERFACE_H__
+#define __HDA_TPLG_INTERFACE_H__
+
+/*
+ * Default types range from 0~12. type can range from 0 to 0xff
+ * SST types start at higher to avoid any overlapping in future
+ */
+#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100
+#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101
+#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101
+#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103
+
+#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
+#define MAX_IN_QUEUE 8
+#define MAX_OUT_QUEUE 8
+
+/* Event types goes here */
+/* Reserve event type 0 for no event handlers */
+enum skl_event_types {
+ SKL_EVENT_NONE = 0,
+ SKL_MIXER_EVENT,
+ SKL_MUX_EVENT,
+ SKL_VMIXER_EVENT,
+ SKL_PGA_EVENT
+};
+
+/**
+ * enum skl_ch_cfg - channel configuration
+ *
+ * @SKL_CH_CFG_MONO: One channel only
+ * @SKL_CH_CFG_STEREO: L & R
+ * @SKL_CH_CFG_2_1: L, R & LFE
+ * @SKL_CH_CFG_3_0: L, C & R
+ * @SKL_CH_CFG_3_1: L, C, R & LFE
+ * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs
+ * @SKL_CH_CFG_4_0: L, C, R & Cs
+ * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs
+ * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE
+ * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two
+ * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ]
+ * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ]
+ * @SKL_CH_CFG_INVALID: Invalid
+ */
+enum skl_ch_cfg {
+ SKL_CH_CFG_MONO = 0,
+ SKL_CH_CFG_STEREO = 1,
+ SKL_CH_CFG_2_1 = 2,
+ SKL_CH_CFG_3_0 = 3,
+ SKL_CH_CFG_3_1 = 4,
+ SKL_CH_CFG_QUATRO = 5,
+ SKL_CH_CFG_4_0 = 6,
+ SKL_CH_CFG_5_0 = 7,
+ SKL_CH_CFG_5_1 = 8,
+ SKL_CH_CFG_DUAL_MONO = 9,
+ SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10,
+ SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11,
+ SKL_CH_CFG_INVALID
+};
+
+enum skl_module_type {
+ SKL_MODULE_TYPE_MIXER = 0,
+ SKL_MODULE_TYPE_COPIER,
+ SKL_MODULE_TYPE_UPDWMIX,
+ SKL_MODULE_TYPE_SRCINT
+};
+
+enum skl_core_affinity {
+ SKL_AFFINITY_CORE_0 = 0,
+ SKL_AFFINITY_CORE_1,
+ SKL_AFFINITY_CORE_MAX
+};
+
+enum skl_pipe_conn_type {
+ SKL_PIPE_CONN_TYPE_NONE = 0,
+ SKL_PIPE_CONN_TYPE_FE,
+ SKL_PIPE_CONN_TYPE_BE
+};
+
+enum skl_hw_conn_type {
+ SKL_CONN_NONE = 0,
+ SKL_CONN_SOURCE = 1,
+ SKL_CONN_SINK = 2
+};
+
+enum skl_dev_type {
+ SKL_DEVICE_BT = 0x0,
+ SKL_DEVICE_DMIC = 0x1,
+ SKL_DEVICE_I2S = 0x2,
+ SKL_DEVICE_SLIMBUS = 0x3,
+ SKL_DEVICE_HDALINK = 0x4,
+ SKL_DEVICE_HDAHOST = 0x5,
+ SKL_DEVICE_NONE
+};
+
+struct skl_dfw_module_pin {
+ u16 module_id;
+ u16 instance_id;
+} __packed;
+
+struct skl_dfw_module_fmt {
+ u32 channels;
+ u32 freq;
+ u32 bit_depth;
+ u32 valid_bit_depth;
+ u32 ch_cfg;
+} __packed;
+
+struct skl_dfw_module_caps {
+ u32 caps_size;
+ u32 caps[HDA_SST_CFG_MAX];
+};
+
+struct skl_dfw_pipe {
+ u8 pipe_id;
+ u8 pipe_priority;
+ u16 conn_type;
+ u32 memory_pages;
+} __packed;
+
+struct skl_dfw_module {
+ u16 module_id;
+ u16 instance_id;
+ u32 max_mcps;
+ u8 core_id;
+ u8 max_in_queue;
+ u8 max_out_queue;
+ u8 is_loadable;
+ u8 conn_type;
+ u8 dev_type;
+ u8 hw_conn_type;
+ u8 time_slot;
+ u32 obs;
+ u32 ibs;
+ u32 params_fixup;
+ u32 converter;
+ u32 module_type;
+ u32 vbus_id;
+ u8 is_dynamic_in_pin;
+ u8 is_dynamic_out_pin;
+ struct skl_dfw_pipe pipe;
+ struct skl_dfw_module_fmt in_fmt;
+ struct skl_dfw_module_fmt out_fmt;
+ struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
+ struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
+ struct skl_dfw_module_caps caps;
+} __packed;
+
+struct skl_dfw_algo_data {
+ u32 max;
+ char *params;
+} __packed;
+
+#endif
diff --git a/kernel/sound/soc/intel/skylake/skl.c b/kernel/sound/soc/intel/skylake/skl.c
new file mode 100644
index 000000000..caa69c459
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl.c
@@ -0,0 +1,558 @@
+/*
+ * skl.c - Implementation of ASoC Intel SKL HD Audio driver
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ *
+ * Derived mostly from Intel HDA driver with following copyrights:
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/pcm.h>
+#include "skl.h"
+
+/*
+ * initialize the PCI registers
+ */
+static void skl_update_pci_byte(struct pci_dev *pci, unsigned int reg,
+ unsigned char mask, unsigned char val)
+{
+ unsigned char data;
+
+ pci_read_config_byte(pci, reg, &data);
+ data &= ~mask;
+ data |= (val & mask);
+ pci_write_config_byte(pci, reg, data);
+}
+
+static void skl_init_pci(struct skl *skl)
+{
+ struct hdac_ext_bus *ebus = &skl->ebus;
+
+ /*
+ * Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+ * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+ * Ensuring these bits are 0 clears playback static on some HD Audio
+ * codecs.
+ * The PCI register TCSEL is defined in the Intel manuals.
+ */
+ dev_dbg(ebus_to_hbus(ebus)->dev, "Clearing TCSEL\n");
+ skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0);
+}
+
+/* called from IRQ */
+static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
+{
+ snd_pcm_period_elapsed(hstr->substream);
+}
+
+static irqreturn_t skl_interrupt(int irq, void *dev_id)
+{
+ struct hdac_ext_bus *ebus = dev_id;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ u32 status;
+
+ if (!pm_runtime_active(bus->dev))
+ return IRQ_NONE;
+
+ spin_lock(&bus->reg_lock);
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ if (status == 0 || status == 0xffffffff) {
+ spin_unlock(&bus->reg_lock);
+ return IRQ_NONE;
+ }
+
+ /* clear rirb int */
+ status = snd_hdac_chip_readb(bus, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ if (status & RIRB_INT_RESPONSE)
+ snd_hdac_bus_update_rirb(bus);
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+ }
+
+ spin_unlock(&bus->reg_lock);
+
+ return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+static irqreturn_t skl_threaded_handler(int irq, void *dev_id)
+{
+ struct hdac_ext_bus *ebus = dev_id;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ u32 status;
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+
+ snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update);
+
+ return IRQ_HANDLED;
+}
+
+static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ int ret;
+
+ ret = request_threaded_irq(skl->pci->irq, skl_interrupt,
+ skl_threaded_handler,
+ IRQF_SHARED,
+ KBUILD_MODNAME, ebus);
+ if (ret) {
+ dev_err(bus->dev,
+ "unable to grab IRQ %d, disabling device\n",
+ skl->pci->irq);
+ return ret;
+ }
+
+ bus->irq = skl->pci->irq;
+ pci_intx(skl->pci, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * power management
+ */
+static int skl_suspend(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_bus_enter_link_reset(bus);
+
+ return 0;
+}
+
+static int skl_resume(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *hda = ebus_to_skl(ebus);
+
+ skl_init_pci(hda);
+
+ snd_hdac_bus_init_chip(bus, 1);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int skl_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+ int ret;
+
+ dev_dbg(bus->dev, "in %s\n", __func__);
+
+ /* enable controller wake up event */
+ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
+
+ snd_hdac_ext_bus_link_power_down_all(ebus);
+
+ ret = skl_suspend_dsp(skl);
+ if (ret < 0)
+ return ret;
+
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_bus_enter_link_reset(bus);
+
+ return 0;
+}
+
+static int skl_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+ int status;
+
+ dev_dbg(bus->dev, "in %s\n", __func__);
+
+ /* Read STATESTS before controller reset */
+ status = snd_hdac_chip_readw(bus, STATESTS);
+
+ skl_init_pci(skl);
+ snd_hdac_bus_init_chip(bus, true);
+ /* disable controller Wake Up event */
+ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
+
+ return skl_resume_dsp(skl);
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops skl_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume)
+ SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL)
+};
+
+/*
+ * destructor
+ */
+static int skl_free(struct hdac_ext_bus *ebus)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ skl->init_failed = 1; /* to be sure */
+
+ snd_hdac_ext_stop_streams(ebus);
+
+ if (bus->irq >= 0)
+ free_irq(bus->irq, (void *)bus);
+ if (bus->remap_addr)
+ iounmap(bus->remap_addr);
+
+ snd_hdac_bus_free_stream_pages(bus);
+ snd_hdac_stream_free_all(ebus);
+ snd_hdac_link_free_all(ebus);
+ pci_release_regions(skl->pci);
+ pci_disable_device(skl->pci);
+
+ snd_hdac_ext_bus_exit(ebus);
+
+ return 0;
+}
+
+static int skl_dmic_device_register(struct skl *skl)
+{
+ struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
+ struct platform_device *pdev;
+ int ret;
+
+ /* SKL has one dmic port, so allocate dmic device for this */
+ pdev = platform_device_alloc("dmic-codec", -1);
+ if (!pdev) {
+ dev_err(bus->dev, "failed to allocate dmic device\n");
+ return -ENOMEM;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(bus->dev, "failed to add dmic device: %d\n", ret);
+ platform_device_put(pdev);
+ return ret;
+ }
+ skl->dmic_dev = pdev;
+
+ return 0;
+}
+
+static void skl_dmic_device_unregister(struct skl *skl)
+{
+ if (skl->dmic_dev)
+ platform_device_unregister(skl->dmic_dev);
+}
+
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct hdac_ext_bus *ebus, int addr)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res;
+
+ mutex_lock(&bus->cmd_mutex);
+ snd_hdac_bus_send_cmd(bus, cmd);
+ snd_hdac_bus_get_response(bus, addr, &res);
+ mutex_unlock(&bus->cmd_mutex);
+ if (res == -1)
+ return -EIO;
+ dev_dbg(bus->dev, "codec #%d probed OK\n", addr);
+
+ return snd_hdac_ext_bus_device_init(ebus, addr);
+}
+
+/* Codec initialization */
+static int skl_codec_create(struct hdac_ext_bus *ebus)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ int c, max_slots;
+
+ max_slots = HDA_MAX_CODECS;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((bus->codec_mask & (1 << c))) {
+ if (probe_codec(ebus, c) < 0) {
+ /*
+ * Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(bus->dev,
+ "Codec #%d probe error; disabling it...\n", c);
+ bus->codec_mask &= ~(1 << c);
+ /*
+ * More badly, accessing to a non-existing
+ * codec often screws up the controller bus,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller bus to get
+ * back to the sanity state.
+ */
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_bus_init_chip(bus, true);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct hdac_bus_ops bus_core_ops = {
+ .command = snd_hdac_bus_send_cmd,
+ .get_response = snd_hdac_bus_get_response,
+};
+
+/*
+ * constructor
+ */
+static int skl_create(struct pci_dev *pci,
+ const struct hdac_io_ops *io_ops,
+ struct skl **rskl)
+{
+ struct skl *skl;
+ struct hdac_ext_bus *ebus;
+
+ int err;
+
+ *rskl = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ skl = devm_kzalloc(&pci->dev, sizeof(*skl), GFP_KERNEL);
+ if (!skl) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ ebus = &skl->ebus;
+ snd_hdac_ext_bus_init(ebus, &pci->dev, &bus_core_ops, io_ops);
+ ebus->bus.use_posbuf = 1;
+ skl->pci = pci;
+
+ ebus->bus.bdl_pos_adj = 0;
+
+ *rskl = skl;
+
+ return 0;
+}
+
+static int skl_first_init(struct hdac_ext_bus *ebus)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct pci_dev *pci = skl->pci;
+ int err;
+ unsigned short gcap;
+ int cp_streams, pb_streams, start_idx;
+
+ err = pci_request_regions(pci, "Skylake HD audio");
+ if (err < 0)
+ return err;
+
+ bus->addr = pci_resource_start(pci, 0);
+ bus->remap_addr = pci_ioremap_bar(pci, 0);
+ if (bus->remap_addr == NULL) {
+ dev_err(bus->dev, "ioremap error\n");
+ return -ENXIO;
+ }
+
+ snd_hdac_ext_bus_parse_capabilities(ebus);
+
+ if (skl_acquire_irq(ebus, 0) < 0)
+ return -EBUSY;
+
+ pci_set_master(pci);
+ synchronize_irq(bus->irq);
+
+ gcap = snd_hdac_chip_readw(bus, GCAP);
+ dev_dbg(bus->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ /* allow 64bit DMA address if supported by H/W */
+ if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) {
+ dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(bus->dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32));
+ }
+
+ /* read number of streams from GCAP register */
+ cp_streams = (gcap >> 8) & 0x0f;
+ pb_streams = (gcap >> 12) & 0x0f;
+
+ if (!pb_streams && !cp_streams)
+ return -EIO;
+
+ ebus->num_streams = cp_streams + pb_streams;
+
+ /* initialize streams */
+ snd_hdac_ext_stream_init_all
+ (ebus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
+ start_idx = cp_streams;
+ snd_hdac_ext_stream_init_all
+ (ebus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
+
+ err = snd_hdac_bus_alloc_stream_pages(bus);
+ if (err < 0)
+ return err;
+
+ /* initialize chip */
+ skl_init_pci(skl);
+
+ snd_hdac_bus_init_chip(bus, true);
+
+ /* codec detection */
+ if (!bus->codec_mask) {
+ dev_err(bus->dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int skl_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct skl *skl;
+ struct hdac_ext_bus *ebus = NULL;
+ struct hdac_bus *bus = NULL;
+ int err;
+
+ /* we use ext core ops, so provide NULL for ops here */
+ err = skl_create(pci, NULL, &skl);
+ if (err < 0)
+ return err;
+
+ ebus = &skl->ebus;
+ bus = ebus_to_hbus(ebus);
+
+ err = skl_first_init(ebus);
+ if (err < 0)
+ goto out_free;
+
+ skl->nhlt = skl_nhlt_init(bus->dev);
+
+ if (skl->nhlt == NULL)
+ goto out_free;
+
+ pci_set_drvdata(skl->pci, ebus);
+
+ /* check if dsp is there */
+ if (ebus->ppcap) {
+ err = skl_init_dsp(skl);
+ if (err < 0) {
+ dev_dbg(bus->dev, "error failed to register dsp\n");
+ goto out_free;
+ }
+ }
+ if (ebus->mlcap)
+ snd_hdac_ext_bus_get_ml_capabilities(ebus);
+
+ /* create device for soc dmic */
+ err = skl_dmic_device_register(skl);
+ if (err < 0)
+ goto out_dsp_free;
+
+ /* register platform dai and controls */
+ err = skl_platform_register(bus->dev);
+ if (err < 0)
+ goto out_dmic_free;
+
+ /* create codec instances */
+ err = skl_codec_create(ebus);
+ if (err < 0)
+ goto out_unregister;
+
+ /*configure PM */
+ pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(bus->dev);
+ pm_runtime_put_noidle(bus->dev);
+ pm_runtime_allow(bus->dev);
+
+ return 0;
+
+out_unregister:
+ skl_platform_unregister(bus->dev);
+out_dmic_free:
+ skl_dmic_device_unregister(skl);
+out_dsp_free:
+ skl_free_dsp(skl);
+out_free:
+ skl->init_failed = 1;
+ skl_free(ebus);
+
+ return err;
+}
+
+static void skl_remove(struct pci_dev *pci)
+{
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ if (skl->tplg)
+ release_firmware(skl->tplg);
+
+ if (pci_dev_run_wake(pci))
+ pm_runtime_get_noresume(&pci->dev);
+ pci_dev_put(pci);
+ skl_platform_unregister(&pci->dev);
+ skl_free_dsp(skl);
+ skl_dmic_device_unregister(skl);
+ skl_free(ebus);
+ dev_set_drvdata(&pci->dev, NULL);
+}
+
+/* PCI IDs */
+static const struct pci_device_id skl_ids[] = {
+ /* Sunrise Point-LP */
+ { PCI_DEVICE(0x8086, 0x9d70), 0},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, skl_ids);
+
+/* pci_driver definition */
+static struct pci_driver skl_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = skl_ids,
+ .probe = skl_probe,
+ .remove = skl_remove,
+ .driver = {
+ .pm = &skl_pm,
+ },
+};
+module_pci_driver(skl_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Skylake ASoC HDA driver");
diff --git a/kernel/sound/soc/intel/skylake/skl.h b/kernel/sound/soc/intel/skylake/skl.h
new file mode 100644
index 000000000..a0709e344
--- /dev/null
+++ b/kernel/sound/soc/intel/skylake/skl.h
@@ -0,0 +1,97 @@
+/*
+ * skl.h - HD Audio skylake defintions.
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef __SOUND_SOC_SKL_H
+#define __SOUND_SOC_SKL_H
+
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include "skl-nhlt.h"
+
+#define SKL_SUSPEND_DELAY 2000
+
+/* Vendor Specific Registers */
+#define AZX_REG_VS_EM1 0x1000
+#define AZX_REG_VS_INRC 0x1004
+#define AZX_REG_VS_OUTRC 0x1008
+#define AZX_REG_VS_FIFOTRK 0x100C
+#define AZX_REG_VS_FIFOTRK2 0x1010
+#define AZX_REG_VS_EM2 0x1030
+#define AZX_REG_VS_EM3L 0x1038
+#define AZX_REG_VS_EM3U 0x103C
+#define AZX_REG_VS_EM4L 0x1040
+#define AZX_REG_VS_EM4U 0x1044
+#define AZX_REG_VS_LTRC 0x1048
+#define AZX_REG_VS_D0I3C 0x104A
+#define AZX_REG_VS_PCE 0x104B
+#define AZX_REG_VS_L2MAGC 0x1050
+#define AZX_REG_VS_L2LAHPT 0x1054
+#define AZX_REG_VS_SDXDPIB_XBASE 0x1084
+#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20
+#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
+#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
+
+struct skl_dsp_resource {
+ u32 max_mcps;
+ u32 max_mem;
+ u32 mcps;
+ u32 mem;
+};
+
+struct skl {
+ struct hdac_ext_bus ebus;
+ struct pci_dev *pci;
+
+ unsigned int init_failed:1; /* delayed init failed */
+ struct platform_device *dmic_dev;
+
+ void *nhlt; /* nhlt ptr */
+ struct skl_sst *skl_sst; /* sst skl ctx */
+
+ struct skl_dsp_resource resource;
+ struct list_head ppl_list;
+ struct list_head dapm_path_list;
+
+ const struct firmware *tplg;
+};
+
+#define skl_to_ebus(s) (&(s)->ebus)
+#define ebus_to_skl(sbus) \
+ container_of(sbus, struct skl, sbus)
+
+/* to pass dai dma data */
+struct skl_dma_params {
+ u32 format;
+ u8 stream_tag;
+};
+
+int skl_platform_unregister(struct device *dev);
+int skl_platform_register(struct device *dev);
+
+void *skl_nhlt_init(struct device *dev);
+void skl_nhlt_free(void *addr);
+struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
+ u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
+
+int skl_init_dsp(struct skl *skl);
+void skl_free_dsp(struct skl *skl);
+int skl_suspend_dsp(struct skl *skl);
+int skl_resume_dsp(struct skl *skl);
+#endif /* __SOUND_SOC_SKL_H */
diff --git a/kernel/sound/soc/jz4740/jz4740-i2s.c b/kernel/sound/soc/jz4740/jz4740-i2s.c
index b05fb1c1a..794a3499e 100644
--- a/kernel/sound/soc/jz4740/jz4740-i2s.c
+++ b/kernel/sound/soc/jz4740/jz4740-i2s.c
@@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = {
{ .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, jz4740_of_matches);
#endif
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
diff --git a/kernel/sound/soc/kirkwood/armada-370-db.c b/kernel/sound/soc/kirkwood/armada-370-db.c
index de7563bdc..e0304d544 100644
--- a/kernel/sound/soc/kirkwood/armada-370-db.c
+++ b/kernel/sound/soc/kirkwood/armada-370-db.c
@@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = {
{ .compatible = "marvell,a370db-audio" },
{ },
};
+MODULE_DEVICE_TABLE(of, a370db_dt_ids);
static struct platform_driver a370db_driver = {
.driver = {
diff --git a/kernel/sound/soc/kirkwood/kirkwood-dma.c b/kernel/sound/soc/kirkwood/kirkwood-dma.c
index 4cf224595..dbfdfe99c 100644
--- a/kernel/sound/soc/kirkwood/kirkwood-dma.c
+++ b/kernel/sound/soc/kirkwood/kirkwood-dma.c
@@ -148,10 +148,14 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
dram = mv_mbus_dram_info();
addr = substream->dma_buffer.addr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (priv->substream_play)
+ return -EBUSY;
priv->substream_play = substream;
kirkwood_dma_conf_mbus_windows(priv->io,
KIRKWOOD_PLAYBACK_WIN, addr, dram);
} else {
+ if (priv->substream_rec)
+ return -EBUSY;
priv->substream_rec = substream;
kirkwood_dma_conf_mbus_windows(priv->io,
KIRKWOOD_RECORD_WIN, addr, dram);
diff --git a/kernel/sound/soc/mediatek/Kconfig b/kernel/sound/soc/mediatek/Kconfig
new file mode 100644
index 000000000..15c04e2ea
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/mediatek/Makefile b/kernel/sound/soc/mediatek/Makefile
new file mode 100644
index 000000000..75effbec4
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/mediatek/mt8173-max98090.c b/kernel/sound/soc/mediatek/mt8173-max98090.c
new file mode 100644
index 000000000..71a1a3504
--- /dev/null
+++ b/kernel/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",
+ .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",
+ .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",
+ .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",
+ .owner = THIS_MODULE,
+ .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, *platform_node;
+ int ret, i;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < card->num_links; i++) {
+ if (mt8173_max98090_dais[i].platform_name)
+ continue;
+ mt8173_max98090_dais[i].platform_of_node = platform_node;
+ }
+
+ 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 = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+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",
+ .of_match_table = mt8173_max98090_dt_match,
+#ifdef CONFIG_PM
+ .pm = &snd_soc_pm_ops,
+#endif
+ },
+ .probe = mt8173_max98090_dev_probe,
+};
+
+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/kernel/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/kernel/sound/soc/mediatek/mt8173-rt5650-rt5676.c
new file mode 100644
index 000000000..50ba538ec
--- /dev/null
+++ b/kernel/sound/soc/mediatek/mt8173-rt5650-rt5676.c
@@ -0,0 +1,280 @@
+/*
+ * 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",
+ .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",
+ .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",
+ .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",
+ .owner = THIS_MODULE,
+ .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;
+ struct device_node *platform_node;
+ int i, ret;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (mt8173_rt5650_rt5676_dais[i].platform_name)
+ continue;
+ mt8173_rt5650_rt5676_dais[i].platform_of_node = platform_node;
+ }
+
+ 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 = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+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",
+ .of_match_table = mt8173_rt5650_rt5676_dt_match,
+#ifdef CONFIG_PM
+ .pm = &snd_soc_pm_ops,
+#endif
+ },
+ .probe = mt8173_rt5650_rt5676_dev_probe,
+};
+
+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/kernel/sound/soc/mediatek/mtk-afe-common.h b/kernel/sound/soc/mediatek/mtk-afe-common.h
new file mode 100644
index 000000000..cc4393cb1
--- /dev/null
+++ b/kernel/sound/soc/mediatek/mtk-afe-common.h
@@ -0,0 +1,101 @@
+/*
+ * 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;
+};
+
+#endif
diff --git a/kernel/sound/soc/mediatek/mtk-afe-pcm.c b/kernel/sound/soc/mediatek/mtk-afe-pcm.c
new file mode 100644
index 000000000..f5baf3c38
--- /dev/null
+++ b/kernel/sound/soc/mediatek/mtk-afe-pcm.c
@@ -0,0 +1,1317 @@
+/*
+ * 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_DL1_END 0x0048
+#define AFE_DL2_BASE 0x0050
+#define AFE_DL2_CUR 0x0054
+#define AFE_AWB_BASE 0x0070
+#define AFE_AWB_CUR 0x007c
+#define AFE_VUL_BASE 0x0080
+#define AFE_VUL_CUR 0x008c
+#define AFE_VUL_END 0x0088
+#define AFE_DAI_BASE 0x0090
+#define AFE_DAI_CUR 0x009c
+#define AFE_MOD_PCM_BASE 0x0330
+#define AFE_MOD_PCM_CUR 0x033c
+#define AFE_HDMI_OUT_BASE 0x0374
+#define AFE_HDMI_OUT_CUR 0x0378
+#define AFE_HDMI_OUT_END 0x037c
+
+#define AFE_ADDA2_TOP_CON0 0x0600
+
+#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 unsigned int mtk_afe_backup_list[] = {
+ AUDIO_TOP_CON0,
+ AFE_CONN1,
+ AFE_CONN2,
+ AFE_CONN7,
+ AFE_CONN8,
+ AFE_DAC_CON1,
+ AFE_DL1_BASE,
+ AFE_DL1_END,
+ AFE_VUL_BASE,
+ AFE_VUL_END,
+ AFE_HDMI_OUT_BASE,
+ AFE_HDMI_OUT_END,
+ AFE_HDMI_CONN0,
+ AFE_DAC_CON0,
+};
+
+struct mtk_afe {
+ /* address for ioremap audio hardware register */
+ void __iomem *base_addr;
+ struct device *dev;
+ struct regmap *regmap;
+ struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
+ struct clk *clocks[MTK_CLK_NUM];
+ unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
+ bool suspended;
+};
+
+static const struct snd_pcm_hardware mtk_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .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);
+
+ /*
+ * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+ * smaller than period_size due to AFE's internal buffer.
+ * This easily leads to overrun when avail_min is period_size.
+ * One more period can hold the possible unread buffer.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS,
+ 3,
+ mtk_afe_hardware.periods_max);
+ if (ret < 0) {
+ dev_err(afe->dev, "hw_constraint_minmax failed\n");
+ return ret;
+ }
+ }
+ 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 int mtk_afe_runtime_suspend(struct device *dev);
+static int mtk_afe_runtime_resume(struct device *dev);
+
+static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+ struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int i;
+
+ dev_dbg(afe->dev, "%s\n", __func__);
+ if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
+ regmap_read(afe->regmap, mtk_afe_backup_list[i],
+ &afe->backup_regs[i]);
+
+ afe->suspended = true;
+ mtk_afe_runtime_suspend(afe->dev);
+ return 0;
+}
+
+static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+ struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int i = 0;
+
+ dev_dbg(afe->dev, "%s\n", __func__);
+ if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
+ return 0;
+
+ mtk_afe_runtime_resume(afe->dev);
+
+ for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
+ regmap_write(afe->regmap, mtk_afe_backup_list[i],
+ afe->backup_regs[i]);
+
+ afe->suspended = false;
+ return 0;
+}
+
+static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1", /* downlink 1 */
+ .id = MTK_AFE_MEMIF_DL1,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .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,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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[] = {
+ /* 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_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_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);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mtk_afe_runtime_suspend(&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",
+ .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/kernel/sound/soc/mxs/mxs-sgtl5000.c b/kernel/sound/soc/mxs/mxs-sgtl5000.c
index 6e6fce6a1..2b23ffbac 100644
--- a/kernel/sound/soc/mxs/mxs-sgtl5000.c
+++ b/kernel/sound/soc/mxs/mxs-sgtl5000.c
@@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
@@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
static int mxs_sgtl5000_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
mxs_saif_put_mclk(0);
- snd_soc_unregister_card(card);
-
return 0;
}
diff --git a/kernel/sound/soc/nuc900/nuc900-pcm.c b/kernel/sound/soc/nuc900/nuc900-pcm.c
index 5ae5ca15b..e09326158 100644
--- a/kernel/sound/soc/nuc900/nuc900-pcm.c
+++ b/kernel/sound/soc/nuc900/nuc900-pcm.c
@@ -308,13 +308,7 @@ static struct snd_soc_platform_driver nuc900_soc_platform = {
static int nuc900_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
-}
-
-static int nuc900_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
}
static struct platform_driver nuc900_pcm_driver = {
@@ -323,7 +317,6 @@ static struct platform_driver nuc900_pcm_driver = {
},
.probe = nuc900_soc_platform_probe,
- .remove = nuc900_soc_platform_remove,
};
module_platform_driver(nuc900_pcm_driver);
diff --git a/kernel/sound/soc/omap/Kconfig b/kernel/sound/soc/omap/Kconfig
index 30d010970..5185a3844 100644
--- a/kernel/sound/soc/omap/Kconfig
+++ b/kernel/sound/soc/omap/Kconfig
@@ -24,7 +24,7 @@ config SND_OMAP_SOC_HDMI_AUDIO
component also under DSS HDMI device. Dummy codec is used as
as codec component. The hdmi audio driver implements also
the card and registers it under its own platform device.
- The device for the dirver is registered by OMAPDSS hdmi
+ The device for the driver is registered by OMAPDSS hdmi
driver.
config SND_OMAP_SOC_N810
diff --git a/kernel/sound/soc/omap/mcbsp.c b/kernel/sound/soc/omap/mcbsp.c
index 68a125205..c7563e230 100644
--- a/kernel/sound/soc/omap/mcbsp.c
+++ b/kernel/sound/soc/omap/mcbsp.c
@@ -965,25 +965,15 @@ int omap_mcbsp_init(struct platform_device *pdev)
mcbsp->free = true;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (!res) {
+ if (!res)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(mcbsp->dev, "invalid memory resource\n");
- return -ENOMEM;
- }
- }
- if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
- dev_name(&pdev->dev))) {
- dev_err(mcbsp->dev, "memory region already claimed\n");
- return -ENODEV;
- }
+
+ mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mcbsp->io_base))
+ return PTR_ERR(mcbsp->io_base);
mcbsp->phys_base = res->start;
mcbsp->reg_cache_size = resource_size(res);
- mcbsp->io_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!mcbsp->io_base)
- return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
if (!res)
diff --git a/kernel/sound/soc/omap/n810.c b/kernel/sound/soc/omap/n810.c
index dcb5336b5..190f868e7 100644
--- a/kernel/sound/soc/omap/n810.c
+++ b/kernel/sound/soc/omap/n810.c
@@ -99,8 +99,7 @@ static int n810_startup(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
n810_ext_control(&rtd->card->dapm);
return clk_prepare_enable(sys_clkout2);
diff --git a/kernel/sound/soc/omap/omap-hdmi-audio.c b/kernel/sound/soc/omap/omap-hdmi-audio.c
index 4775da4c4..584b23723 100644
--- a/kernel/sound/soc/omap/omap-hdmi-audio.c
+++ b/kernel/sound/soc/omap/omap-hdmi-audio.c
@@ -81,7 +81,15 @@ static int hdmi_dai_startup(struct snd_pcm_substream *substream,
ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
if (ret < 0) {
- dev_err(dai->dev, "could not apply constraint\n");
+ dev_err(dai->dev, "Could not apply period constraint: %d\n",
+ ret);
+ return ret;
+ }
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
+ if (ret < 0) {
+ dev_err(dai->dev, "Could not apply buffer constraint: %d\n",
+ ret);
return ret;
}
@@ -210,16 +218,18 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
cea->db3 = 0; /* not used, all zeros */
- /*
- * The OMAP HDMI IP requires to use the 8-channel channel code when
- * transmitting more than two channels.
- */
if (params_channels(params) == 2)
cea->db4_ca = 0x0;
+ else if (params_channels(params) == 6)
+ cea->db4_ca = 0xb;
else
cea->db4_ca = 0x13;
- cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
+ if (cea->db4_ca == 0x00)
+ cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED;
+ else
+ cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
+
/* the expression is trivial but makes clear what we are doing */
cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
diff --git a/kernel/sound/soc/omap/omap-twl4030.c b/kernel/sound/soc/omap/omap-twl4030.c
index 3673ada43..743131473 100644
--- a/kernel/sound/soc/omap/omap-twl4030.c
+++ b/kernel/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/kernel/sound/soc/omap/omap3pandora.c b/kernel/sound/soc/omap/omap3pandora.c
index 076bec606..732e749a1 100644
--- a/kernel/sound/soc/omap/omap3pandora.c
+++ b/kernel/sound/soc/omap/omap3pandora.c
@@ -154,8 +154,7 @@ static const struct snd_soc_dapm_route omap3pandora_map[] = {
static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
/* All TWL4030 output pins are floating */
snd_soc_dapm_nc_pin(dapm, "EARPIECE");
@@ -174,8 +173,7 @@ static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
/* Not comnnected */
snd_soc_dapm_nc_pin(dapm, "HSMIC");
diff --git a/kernel/sound/soc/omap/rx51.c b/kernel/sound/soc/omap/rx51.c
index c2ddf0fbf..5e21f0857 100644
--- a/kernel/sound/soc/omap/rx51.c
+++ b/kernel/sound/soc/omap/rx51.c
@@ -107,8 +107,7 @@ static int rx51_startup(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
rx51_ext_control(&card->dapm);
return 0;
@@ -245,6 +244,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,21 +289,14 @@ 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");
return err;
}
- snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
+ snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
err = omap_mcbsp_st_add_controls(rtd, 2);
if (err < 0) {
@@ -383,6 +377,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 +450,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/kernel/sound/soc/pxa/Kconfig b/kernel/sound/soc/pxa/Kconfig
index 39cea8084..f2bf8661d 100644
--- a/kernel/sound/soc/pxa/Kconfig
+++ b/kernel/sound/soc/pxa/Kconfig
@@ -1,7 +1,6 @@
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
depends on ARCH_PXA
- select SND_ARM
select SND_PXA2XX_LIB
help
Say Y or M if you want to add support for codecs attached to
@@ -25,7 +24,6 @@ config SND_PXA2XX_AC97
config SND_PXA2XX_SOC_AC97
tristate
select AC97_BUS
- select SND_ARM
select SND_PXA2XX_LIB_AC97
select SND_SOC_AC97_BUS
diff --git a/kernel/sound/soc/pxa/brownstone.c b/kernel/sound/soc/pxa/brownstone.c
index 79936e3e8..6147e86e9 100644
--- a/kernel/sound/soc/pxa/brownstone.c
+++ b/kernel/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)
@@ -139,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev)
int ret;
brownstone.dev = &pdev->dev;
- ret = snd_soc_register_card(&brownstone);
+ ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int brownstone_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&brownstone);
- return 0;
-}
-
static struct platform_driver mmp_driver = {
.driver = {
.name = "brownstone-audio",
.pm = &snd_soc_pm_ops,
},
.probe = brownstone_probe,
- .remove = brownstone_remove,
};
module_platform_driver(mmp_driver);
diff --git a/kernel/sound/soc/pxa/corgi.c b/kernel/sound/soc/pxa/corgi.c
index 3580d10c9..c97dc13d3 100644
--- a/kernel/sound/soc/pxa/corgi.c
+++ b/kernel/sound/soc/pxa/corgi.c
@@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int corgi_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver corgi_driver = {
.driver = {
.name = "corgi-audio",
.pm = &snd_soc_pm_ops,
},
.probe = corgi_probe,
- .remove = corgi_remove,
};
module_platform_driver(corgi_driver);
diff --git a/kernel/sound/soc/pxa/e740_wm9705.c b/kernel/sound/soc/pxa/e740_wm9705.c
index d72e124a3..1de876529 100644
--- a/kernel/sound/soc/pxa/e740_wm9705.c
+++ b/kernel/sound/soc/pxa/e740_wm9705.c
@@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev)
static int e740_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/kernel/sound/soc/pxa/e750_wm9705.c b/kernel/sound/soc/pxa/e750_wm9705.c
index 48f2d7c2e..b7eb7cd5d 100644
--- a/kernel/sound/soc/pxa/e750_wm9705.c
+++ b/kernel/sound/soc/pxa/e750_wm9705.c
@@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev)
static int e750_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/kernel/sound/soc/pxa/e800_wm9712.c b/kernel/sound/soc/pxa/e800_wm9712.c
index 45d4bd46f..41bf71466 100644
--- a/kernel/sound/soc/pxa/e800_wm9712.c
+++ b/kernel/sound/soc/pxa/e800_wm9712.c
@@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev)
static int e800_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/kernel/sound/soc/pxa/hx4700.c b/kernel/sound/soc/pxa/hx4700.c
index 9f8be7cd5..ecbf2873b 100644
--- a/kernel/sound/soc/pxa/hx4700.c
+++ b/kernel/sound/soc/pxa/hx4700.c
@@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev)
return ret;
snd_soc_card_hx4700.dev = &pdev->dev;
- ret = snd_soc_register_card(&snd_soc_card_hx4700);
+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
if (ret)
gpio_free_array(hx4700_audio_gpios,
ARRAY_SIZE(hx4700_audio_gpios));
@@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
static int hx4700_audio_remove(struct platform_device *pdev)
{
- snd_soc_unregister_card(&snd_soc_card_hx4700);
-
gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
diff --git a/kernel/sound/soc/pxa/imote2.c b/kernel/sound/soc/pxa/imote2.c
index 29fabbfd2..9d0e40771 100644
--- a/kernel/sound/soc/pxa/imote2.c
+++ b/kernel/sound/soc/pxa/imote2.c
@@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int imote2_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver imote2_driver = {
.driver = {
.name = "imote2-audio",
.pm = &snd_soc_pm_ops,
},
.probe = imote2_probe,
- .remove = imote2_remove,
};
module_platform_driver(imote2_driver);
diff --git a/kernel/sound/soc/pxa/mioa701_wm9713.c b/kernel/sound/soc/pxa/mioa701_wm9713.c
index a9615a574..29bc60e85 100644
--- a/kernel/sound/soc/pxa/mioa701_wm9713.c
+++ b/kernel/sound/soc/pxa/mioa701_wm9713.c
@@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
return -ENODEV;
mioa701.dev = &pdev->dev;
- rc = snd_soc_register_card(&mioa701);
+ rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
if (!rc)
dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
"lead to overheating and possible destruction of your device."
@@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
return rc;
}
-static int mioa701_wm9713_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver mioa701_wm9713_driver = {
.probe = mioa701_wm9713_probe,
- .remove = mioa701_wm9713_remove,
.driver = {
.name = "mioa701-wm9713",
.pm = &snd_soc_pm_ops,
diff --git a/kernel/sound/soc/pxa/mmp-pcm.c b/kernel/sound/soc/pxa/mmp-pcm.c
index 1eb45dcfb..51e790d00 100644
--- a/kernel/sound/soc/pxa/mmp-pcm.c
+++ b/kernel/sound/soc/pxa/mmp-pcm.c
@@ -232,13 +232,7 @@ static int mmp_pcm_probe(struct platform_device *pdev)
mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
pdata->period_max_capture;
}
- return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
-}
-
-static int mmp_pcm_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
}
static struct platform_driver mmp_pcm_driver = {
@@ -247,7 +241,6 @@ static struct platform_driver mmp_pcm_driver = {
},
.probe = mmp_pcm_probe,
- .remove = mmp_pcm_remove,
};
module_platform_driver(mmp_pcm_driver);
diff --git a/kernel/sound/soc/pxa/palm27x.c b/kernel/sound/soc/pxa/palm27x.c
index c20bbc042..4e74d9573 100644
--- a/kernel/sound/soc/pxa/palm27x.c
+++ b/kernel/sound/soc/pxa/palm27x.c
@@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
palm27x_asoc.dev = &pdev->dev;
- ret = snd_soc_register_card(&palm27x_asoc);
+ ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int palm27x_asoc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&palm27x_asoc);
- return 0;
-}
-
static struct platform_driver palm27x_wm9712_driver = {
.probe = palm27x_asoc_probe,
- .remove = palm27x_asoc_remove,
.driver = {
.name = "palm27x-asoc",
.pm = &snd_soc_pm_ops,
diff --git a/kernel/sound/soc/pxa/poodle.c b/kernel/sound/soc/pxa/poodle.c
index 0fce8c420..84d0e2e50 100644
--- a/kernel/sound/soc/pxa/poodle.c
+++ b/kernel/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)
@@ -278,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int poodle_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver poodle_driver = {
.driver = {
.name = "poodle-audio",
.pm = &snd_soc_pm_ops,
},
.probe = poodle_probe,
- .remove = poodle_remove,
};
module_platform_driver(poodle_driver);
diff --git a/kernel/sound/soc/pxa/pxa-ssp.c b/kernel/sound/soc/pxa/pxa-ssp.c
index fbe2e93d6..da03fad1b 100644
--- a/kernel/sound/soc/pxa/pxa-ssp.c
+++ b/kernel/sound/soc/pxa/pxa-ssp.c
@@ -809,18 +809,13 @@ static const struct of_device_id pxa_ssp_of_ids[] = {
{ .compatible = "mrvl,pxa-ssp-dai" },
{}
};
+MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
#endif
static int asoc_ssp_probe(struct platform_device *pdev)
{
- return snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
- &pxa_ssp_dai, 1);
-}
-
-static int asoc_ssp_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_component(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
+ &pxa_ssp_dai, 1);
}
static struct platform_driver asoc_ssp_driver = {
@@ -830,7 +825,6 @@ static struct platform_driver asoc_ssp_driver = {
},
.probe = asoc_ssp_probe,
- .remove = asoc_ssp_remove,
};
module_platform_driver(asoc_ssp_driver);
diff --git a/kernel/sound/soc/pxa/pxa2xx-ac97.c b/kernel/sound/soc/pxa/pxa2xx-ac97.c
index 1f6054650..f3de615aa 100644
--- a/kernel/sound/soc/pxa/pxa2xx-ac97.c
+++ b/kernel/sound/soc/pxa/pxa2xx-ac97.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
@@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_cold_reset,
};
-static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 11,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.filter_data = &pxa2xx_ac97_pcm_stereo_in_req,
};
-static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 12,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.filter_data = &pxa2xx_ac97_pcm_stereo_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 10,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 9,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 8,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.addr = __PREG(MCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req,
};
-static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_48000)
static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_params,
+ .startup = pxa2xx_ac97_hifi_startup,
};
static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_aux_params,
+ .startup = pxa2xx_ac97_aux_startup,
};
static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_mic_params,
+ .startup = pxa2xx_ac97_mic_startup,
};
/*
diff --git a/kernel/sound/soc/pxa/pxa2xx-i2s.c b/kernel/sound/soc/pxa/pxa2xx-i2s.c
index e68290c15..0389cf7b4 100644
--- a/kernel/sound/soc/pxa/pxa2xx-i2s.c
+++ b/kernel/sound/soc/pxa/pxa2xx-i2s.c
@@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
/* Along with FIFO servicing */
SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+ snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
+ &pxa2xx_i2s_pcm_stereo_in);
+
return 0;
}
@@ -367,19 +370,12 @@ static const struct snd_soc_component_driver pxa_i2s_component = {
static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
{
- return snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
- &pxa_i2s_dai, 1);
-}
-
-static int pxa2xx_i2s_drv_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_component(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
+ &pxa_i2s_dai, 1);
}
static struct platform_driver pxa2xx_i2s_driver = {
.probe = pxa2xx_i2s_drv_probe,
- .remove = pxa2xx_i2s_drv_remove,
.driver = {
.name = "pxa2xx-i2s",
diff --git a/kernel/sound/soc/pxa/pxa2xx-pcm.c b/kernel/sound/soc/pxa/pxa2xx-pcm.c
index a51c9da66..9f390398d 100644
--- a/kernel/sound/soc/pxa/pxa2xx-pcm.c
+++ b/kernel/sound/soc/pxa/pxa2xx-pcm.c
@@ -15,8 +15,6 @@
#include <linux/dmaengine.h>
#include <linux/of.h>
-#include <mach/dma.h>
-
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
@@ -27,11 +25,8 @@
static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_dmaengine_dai_dma_data *dma;
- int ret;
dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
@@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
if (!dma)
return 0;
- /* this may get called several times by oss emulation
- * with different params */
- if (prtd->params == NULL) {
- prtd->params = dma;
- ret = pxa_request_dma("name", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- return ret;
- prtd->dma_ch = ret;
- } else if (prtd->params != dma) {
- pxa_free_dma(prtd->dma_ch);
- prtd->params = dma;
- ret = pxa_request_dma("name", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- return ret;
- prtd->dma_ch = ret;
- }
-
return __pxa2xx_pcm_hw_params(substream, params);
}
static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
__pxa2xx_pcm_hw_free(substream);
- if (prtd->dma_ch >= 0) {
- pxa_free_dma(prtd->dma_ch);
- prtd->dma_ch = -1;
- prtd->params = NULL;
- }
-
return 0;
}
@@ -124,13 +92,7 @@ static struct snd_soc_platform_driver pxa2xx_soc_platform = {
static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform);
-}
-
-static int pxa2xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform);
}
#ifdef CONFIG_OF
@@ -138,6 +100,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = {
{ .compatible = "mrvl,pxa-pcm-audio" },
{ }
};
+MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
#endif
static struct platform_driver pxa_pcm_driver = {
@@ -147,7 +110,6 @@ static struct platform_driver pxa_pcm_driver = {
},
.probe = pxa2xx_soc_platform_probe,
- .remove = pxa2xx_soc_platform_remove,
};
module_platform_driver(pxa_pcm_driver);
diff --git a/kernel/sound/soc/pxa/spitz.c b/kernel/sound/soc/pxa/spitz.c
index 461123ad5..b00222620 100644
--- a/kernel/sound/soc/pxa/spitz.c
+++ b/kernel/sound/soc/pxa/spitz.c
@@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -322,9 +322,6 @@ err1:
static int spitz_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
gpio_free(spitz_mic_gpio);
return 0;
}
diff --git a/kernel/sound/soc/pxa/tosa.c b/kernel/sound/soc/pxa/tosa.c
index cb49284e8..49518dd64 100644
--- a/kernel/sound/soc/pxa/tosa.c
+++ b/kernel/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)
@@ -244,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -255,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev)
static int tosa_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free(TOSA_GPIO_L_MUTE);
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/kernel/sound/soc/pxa/ttc-dkb.c b/kernel/sound/soc/pxa/ttc-dkb.c
index 1753c7d9e..65c20f779 100644
--- a/kernel/sound/soc/pxa/ttc-dkb.c
+++ b/kernel/sound/soc/pxa/ttc-dkb.c
@@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev)
return ret;
}
-static int ttc_dkb_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
-}
-
static struct platform_driver ttc_dkb_driver = {
.driver = {
.name = "ttc-dkb-audio",
.pm = &snd_soc_pm_ops,
},
.probe = ttc_dkb_probe,
- .remove = ttc_dkb_remove,
};
module_platform_driver(ttc_dkb_driver);
diff --git a/kernel/sound/soc/pxa/z2.c b/kernel/sound/soc/pxa/z2.c
index bcbfbe830..990b1aa6d 100644
--- a/kernel/sound/soc/pxa/z2.c
+++ b/kernel/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/kernel/sound/soc/qcom/Kconfig b/kernel/sound/soc/qcom/Kconfig
index b07f183fc..3cc252e55 100644
--- a/kernel/sound/soc/qcom/Kconfig
+++ b/kernel/sound/soc/qcom/Kconfig
@@ -1,5 +1,6 @@
config SND_SOC_QCOM
tristate "ASoC support for QCOM platforms"
+ depends on ARCH_QCOM || COMPILE_TEST
help
Say Y or M if you want to add support to use audio devices
in Qualcomm Technologies SOC-based platforms.
@@ -12,12 +13,30 @@ config SND_SOC_LPASS_PLATFORM
tristate
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
+ select SND_SOC_LPASS_CPU
+ select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_LPASS_APQ8016
+ tristate
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
+ 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
+ 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/kernel/sound/soc/qcom/Makefile b/kernel/sound/soc/qcom/Makefile
index c5ce96c76..79e5c50a8 100644
--- a/kernel/sound/soc/qcom/Makefile
+++ b/kernel/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/kernel/sound/soc/qcom/apq8016_sbc.c b/kernel/sound/soc/qcom/apq8016_sbc.c
new file mode 100644
index 000000000..1efdf0088
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/qcom/lpass-apq8016.c b/kernel/sound/soc/qcom/lpass-apq8016.c
new file mode 100644
index 000000000..94efc0102
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/qcom/lpass-cpu.c b/kernel/sound/soc/qcom/lpass-cpu.c
index dc790abaa..e5101e0d2 100644
--- a/kernel/sound/soc/qcom/lpass-cpu.c
+++ b/kernel/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 = {
+const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
.set_sysclk = lpass_cpu_daiops_set_sysclk,
.startup = lpass_cpu_daiops_startup,
.shutdown = lpass_cpu_daiops_shutdown,
@@ -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,39 @@ 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[dai_id]));
+ return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
+ }
}
drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
@@ -439,7 +468,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 +490,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/kernel/sound/soc/qcom/lpass-ipq806x.c b/kernel/sound/soc/qcom/lpass-ipq806x.c
new file mode 100644
index 000000000..7a4167952
--- /dev/null
+++ b/kernel/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;
+}
+
+static 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/kernel/sound/soc/qcom/lpass-lpaif-ipq806x.h b/kernel/sound/soc/qcom/lpass-lpaif-reg.h
index dc423b888..95e22f131 100644
--- a/kernel/sound/soc/qcom/lpass-lpaif-ipq806x.h
+++ b/kernel/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/kernel/sound/soc/qcom/lpass-platform.c b/kernel/sound/soc/qcom/lpass-platform.c
index 2fa6280df..79688aa19 100644
--- a/kernel/sound/soc/qcom/lpass-platform.c
+++ b/kernel/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/kernel/sound/soc/qcom/lpass.h b/kernel/sound/soc/qcom/lpass.h
index 5c99b3dac..0b63e2e5b 100644
--- a/kernel/sound/soc/qcom/lpass.h
+++ b/kernel/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 const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
#endif /* __LPASS_H__ */
diff --git a/kernel/sound/soc/qcom/storm.c b/kernel/sound/soc/qcom/storm.c
index b8bd29619..2d833bffd 100644
--- a/kernel/sound/soc/qcom/storm.c
+++ b/kernel/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/kernel/sound/soc/rockchip/Kconfig b/kernel/sound/soc/rockchip/Kconfig
index e18182699..f1e0c703e 100644
--- a/kernel/sound/soc/rockchip/Kconfig
+++ b/kernel/sound/soc/rockchip/Kconfig
@@ -14,3 +14,30 @@ config SND_SOC_ROCKCHIP_I2S
Say Y or M if you want to add support for I2S driver for
Rockchip I2S device. The device supports upto maximum of
8 channels each for play and record.
+
+config SND_SOC_ROCKCHIP_SPDIF
+ tristate "Rockchip SPDIF Device Driver"
+ depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for SPDIF driver for
+ Rockchip SPDIF transceiver device.
+
+config SND_SOC_ROCKCHIP_MAX98090
+ tristate "ASoC support for Rockchip boards using a MAX98090 codec"
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_MAX98090
+ select SND_SOC_TS3A227E
+ help
+ Say Y or M here if you want to add support for SoC audio on Rockchip
+ boards using the MAX98090 codec, such as Veyron.
+
+config SND_SOC_ROCKCHIP_RT5645
+ tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_RT5645
+ help
+ Say Y or M here if you want to add support for SoC audio on Rockchip
+ boards using the RT5645/RT5650 codec, such as Veyron.
diff --git a/kernel/sound/soc/rockchip/Makefile b/kernel/sound/soc/rockchip/Makefile
index b9219092b..c0bf56012 100644
--- a/kernel/sound/soc/rockchip/Makefile
+++ b/kernel/sound/soc/rockchip/Makefile
@@ -1,4 +1,12 @@
# ROCKCHIP Platform Support
-snd-soc-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-spdif-objs := rockchip_spdif.o
-obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
+
+snd-soc-rockchip-max98090-objs := rockchip_max98090.o
+snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
+
+obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o
diff --git a/kernel/sound/soc/rockchip/rockchip_i2s.c b/kernel/sound/soc/rockchip/rockchip_i2s.c
index acb5be53b..58ee64594 100644
--- a/kernel/sound/soc/rockchip/rockchip_i2s.c
+++ b/kernel/sound/soc/rockchip/rockchip_i2s.c
@@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = to_info(dai);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int val = 0;
switch (params_format(params)) {
@@ -245,13 +246,46 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val);
- regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val);
+ switch (params_channels(params)) {
+ case 8:
+ val |= I2S_CHN_8;
+ break;
+ case 6:
+ val |= I2S_CHN_6;
+ break;
+ case 4:
+ val |= I2S_CHN_4;
+ break;
+ case 2:
+ val |= I2S_CHN_2;
+ break;
+ default:
+ dev_err(i2s->dev, "invalid channel: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_update_bits(i2s->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ val);
+ else
+ regmap_update_bits(i2s->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ val);
+
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
I2S_DMACR_RDL(16));
+ val = I2S_CKR_TRCM_TXRX;
+ if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
+ val = I2S_CKR_TRCM_TXSHARE;
+
+ regmap_update_bits(i2s->regmap, I2S_CKR,
+ I2S_CKR_TRCM_MASK,
+ val);
return 0;
}
@@ -415,10 +449,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
static int rockchip_i2s_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
struct rk_i2s_dev *i2s;
struct resource *res;
void __iomem *regs;
int ret;
+ int val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s) {
@@ -475,6 +511,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}
+ /* refine capture channels */
+ if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
+ if (val >= 2 && val <= 8)
+ rockchip_i2s_dai.capture.channels_max = val;
+ else
+ rockchip_i2s_dai.capture.channels_max = 2;
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component,
&rockchip_i2s_dai, 1);
@@ -483,16 +527,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_suspend;
}
- ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
- goto err_pcm_register;
+ return ret;
}
return 0;
-err_pcm_register:
- snd_dmaengine_pcm_unregister(&pdev->dev);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
i2s_runtime_suspend(&pdev->dev);
@@ -512,8 +554,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
clk_disable_unprepare(i2s->mclk);
clk_disable_unprepare(i2s->hclk);
- snd_dmaengine_pcm_unregister(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
return 0;
}
diff --git a/kernel/sound/soc/rockchip/rockchip_i2s.h b/kernel/sound/soc/rockchip/rockchip_i2s.h
index 93f456f51..dc6e2c74d 100644
--- a/kernel/sound/soc/rockchip/rockchip_i2s.h
+++ b/kernel/sound/soc/rockchip/rockchip_i2s.h
@@ -49,6 +49,9 @@
* RXCR
* receive operation control register
*/
+#define I2S_RXCR_CSR_SHIFT 15
+#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_HWT BIT(14)
#define I2S_RXCR_SJM_SHIFT 12
#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
@@ -75,6 +78,12 @@
* CKR
* clock generation register
*/
+#define I2S_CKR_TRCM_SHIFT 28
+#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
@@ -207,6 +216,13 @@ enum {
ROCKCHIP_DIV_BCLK,
};
+/* channel select */
+#define I2S_CSR_SHIFT 15
+#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
+#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
+#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
+#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
+
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)
diff --git a/kernel/sound/soc/rockchip/rockchip_max98090.c b/kernel/sound/soc/rockchip/rockchip_max98090.c
new file mode 100644
index 000000000..26567b103
--- /dev/null
+++ b/kernel/sound/soc/rockchip/rockchip_max98090.c
@@ -0,0 +1,236 @@
+/*
+ * Rockchip machine ASoC driver for boards using a MAX90809 CODEC.
+ *
+ * Copyright (c) 2014, ROCKCHIP CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "rockchip_i2s.h"
+#include "../codecs/ts3a227e.h"
+
+#define DRV_NAME "rockchip-snd-max98090"
+
+static struct snd_soc_jack headset_jack;
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headset Jack",
+ .mask = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ },
+};
+
+static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route rk_audio_map[] = {
+ {"IN34", NULL, "Headset Mic"},
+ {"IN34", NULL, "MICBIAS"},
+ {"MICBIAS", NULL, "Headset Mic"},
+ {"DMICL", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPL"},
+ {"Headphone", NULL, "HPR"},
+ {"Speaker", NULL, "SPKL"},
+ {"Speaker", NULL, "SPKR"},
+};
+
+static const struct snd_kcontrol_new rk_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int mclk;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 44100:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int rk_init(struct snd_soc_pcm_runtime *runtime)
+{
+ /* Enable Headset and 4 Buttons Jack detection */
+ return snd_soc_card_jack_new(runtime->card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+}
+
+static int rk_98090_headset_init(struct snd_soc_component *component)
+{
+ return ts3a227e_enable_jack_detect(component, &headset_jack);
+}
+
+static struct snd_soc_ops rk_aif1_ops = {
+ .hw_params = rk_aif1_hw_params,
+};
+
+static struct snd_soc_aux_dev rk_98090_headset_dev = {
+ .name = "Headset Chip",
+ .init = rk_98090_headset_init,
+};
+
+static struct snd_soc_dai_link rk_dailink = {
+ .name = "max98090",
+ .stream_name = "Audio",
+ .codec_dai_name = "HiFi",
+ .init = rk_init,
+ .ops = &rk_aif1_ops,
+ /* set max98090 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_card_rk = {
+ .name = "ROCKCHIP-I2S",
+ .owner = THIS_MODULE,
+ .dai_link = &rk_dailink,
+ .num_links = 1,
+ .aux_dev = &rk_98090_headset_dev,
+ .num_aux_devs = 1,
+ .dapm_widgets = rk_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
+ .dapm_routes = rk_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
+ .controls = rk_mc_controls,
+ .num_controls = ARRAY_SIZE(rk_mc_controls),
+};
+
+static int snd_rk_mc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct snd_soc_card *card = &snd_soc_card_rk;
+ struct device_node *np = pdev->dev.of_node;
+
+ /* register the soc card */
+ card->dev = &pdev->dev;
+
+ rk_dailink.codec_of_node = of_parse_phandle(np,
+ "rockchip,audio-codec", 0);
+ if (!rk_dailink.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.cpu_of_node = of_parse_phandle(np,
+ "rockchip,i2s-controller", 0);
+ if (!rk_dailink.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,i2s-controller' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
+
+ rk_98090_headset_dev.codec_of_node = of_parse_phandle(np,
+ "rockchip,headset-codec", 0);
+ if (!rk_98090_headset_dev.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,headset-codec' missing/invalid\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "rockchip,model");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Soc parse card name failed %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Soc register card failed %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id rockchip_max98090_of_match[] = {
+ { .compatible = "rockchip,rockchip-audio-max98090", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_max98090_of_match);
+
+static struct platform_driver snd_rk_mc_driver = {
+ .probe = snd_rk_mc_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = rockchip_max98090_of_match,
+ },
+};
+
+module_platform_driver(snd_rk_mc_driver);
+
+MODULE_AUTHOR("jianqun <jay.xu@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip max98090 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/kernel/sound/soc/rockchip/rockchip_rt5645.c b/kernel/sound/soc/rockchip/rockchip_rt5645.c
new file mode 100644
index 000000000..68c62e4c2
--- /dev/null
+++ b/kernel/sound/soc/rockchip/rockchip_rt5645.c
@@ -0,0 +1,225 @@
+/*
+ * Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC.
+ *
+ * Copyright (c) 2015, ROCKCHIP CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "rockchip_i2s.h"
+
+#define DRV_NAME "rockchip-snd-rt5645"
+
+static struct snd_soc_jack headset_jack;
+
+/* Jack detect via rt5645 driver. */
+extern int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
+ struct snd_soc_jack *btn_jack);
+
+static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rk_audio_map[] = {
+ /* Input Lines */
+ {"DMIC L2", NULL, "Int Mic"},
+ {"DMIC R2", NULL, "Int Mic"},
+ {"RECMIXL", NULL, "Headset Mic"},
+ {"RECMIXR", NULL, "Headset Mic"},
+
+ /* Output Lines */
+ {"Headphones", NULL, "HPOR"},
+ {"Headphones", NULL, "HPOL"},
+ {"Speakers", NULL, "SPOL"},
+ {"Speakers", NULL, "SPOR"},
+};
+
+static const struct snd_kcontrol_new rk_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int mclk;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 44100:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int rk_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ int ret;
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
+ return ret;
+ }
+
+ return rt5645_set_jack_detect(runtime->codec,
+ &headset_jack,
+ &headset_jack,
+ &headset_jack);
+}
+
+static struct snd_soc_ops rk_aif1_ops = {
+ .hw_params = rk_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link rk_dailink = {
+ .name = "rt5645",
+ .stream_name = "rt5645 PCM",
+ .codec_dai_name = "rt5645-aif1",
+ .init = rk_init,
+ .ops = &rk_aif1_ops,
+ /* set rt5645 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_card_rk = {
+ .name = "I2S-RT5650",
+ .owner = THIS_MODULE,
+ .dai_link = &rk_dailink,
+ .num_links = 1,
+ .dapm_widgets = rk_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
+ .dapm_routes = rk_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
+ .controls = rk_mc_controls,
+ .num_controls = ARRAY_SIZE(rk_mc_controls),
+};
+
+static int snd_rk_mc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct snd_soc_card *card = &snd_soc_card_rk;
+ struct device_node *np = pdev->dev.of_node;
+
+ /* register the soc card */
+ card->dev = &pdev->dev;
+
+ rk_dailink.codec_of_node = of_parse_phandle(np,
+ "rockchip,audio-codec", 0);
+ if (!rk_dailink.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.cpu_of_node = of_parse_phandle(np,
+ "rockchip,i2s-controller", 0);
+ if (!rk_dailink.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,i2s-controller' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
+
+ ret = snd_soc_of_parse_card_name(card, "rockchip,model");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Soc parse card name failed %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Soc register card failed %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id rockchip_rt5645_of_match[] = {
+ { .compatible = "rockchip,rockchip-audio-rt5645", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
+
+static struct platform_driver snd_rk_mc_driver = {
+ .probe = snd_rk_mc_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = rockchip_rt5645_of_match,
+ },
+};
+
+module_platform_driver(snd_rk_mc_driver);
+
+MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/kernel/sound/soc/rockchip/rockchip_spdif.c b/kernel/sound/soc/rockchip/rockchip_spdif.c
new file mode 100644
index 000000000..5a806da89
--- /dev/null
+++ b/kernel/sound/soc/rockchip/rockchip_spdif.c
@@ -0,0 +1,407 @@
+/* sound/soc/rockchip/rk_spdif.c
+ *
+ * ALSA SoC Audio Layer - Rockchip I2S Controller driver
+ *
+ * Copyright (c) 2014 Rockchip Electronics Co. Ltd.
+ * Author: Jianqun <jay.xu@rock-chips.com>
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "rockchip_spdif.h"
+
+enum rk_spdif_type {
+ RK_SPDIF_RK3066,
+ RK_SPDIF_RK3188,
+ RK_SPDIF_RK3288,
+};
+
+#define RK3288_GRF_SOC_CON2 0x24c
+
+struct rk_spdif_dev {
+ struct device *dev;
+
+ struct clk *mclk;
+ struct clk *hclk;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+
+ struct regmap *regmap;
+};
+
+static const struct of_device_id rk_spdif_match[] = {
+ { .compatible = "rockchip,rk3066-spdif",
+ .data = (void *) RK_SPDIF_RK3066 },
+ { .compatible = "rockchip,rk3188-spdif",
+ .data = (void *) RK_SPDIF_RK3188 },
+ { .compatible = "rockchip,rk3288-spdif",
+ .data = (void *) RK_SPDIF_RK3288 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk_spdif_match);
+
+static int rk_spdif_runtime_suspend(struct device *dev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+
+ return 0;
+}
+
+static int rk_spdif_runtime_resume(struct device *dev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdif->mclk);
+ if (ret) {
+ dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(spdif->hclk);
+ if (ret) {
+ dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
+ int srate, mclk;
+ int ret;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 32000:
+ case 48000:
+ case 96000:
+ mclk = 96000 * 128; /* 12288000 hz */
+ break;
+ case 44100:
+ mclk = 44100 * 256; /* 11289600 hz */
+ break;
+ case 192000:
+ mclk = 192000 * 128; /* 24576000 hz */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= SPDIF_CFGR_VDW_16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= SPDIF_CFGR_VDW_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= SPDIF_CFGR_VDW_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set clock and calculate divider */
+ ret = clk_set_rate(spdif->mclk, mclk);
+ if (ret != 0) {
+ dev_err(spdif->dev, "Failed to set module clock rate: %d\n",
+ ret);
+ return ret;
+ }
+
+ val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
+ ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
+ SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
+ SDPIF_CFGR_VDW_MASK,
+ val);
+
+ return ret;
+}
+
+static int rk_spdif_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL_MASK,
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL(16));
+
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_START);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+ SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_DISABLE);
+
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_STOP);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &spdif->playback_dma_data;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+ .hw_params = rk_spdif_hw_params,
+ .trigger = rk_spdif_trigger,
+};
+
+static struct snd_soc_dai_driver rk_spdif_dai = {
+ .probe = rk_spdif_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &rk_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver rk_spdif_component = {
+ .name = "rockchip-spdif",
+};
+
+static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_CFGR:
+ case SPDIF_DMACR:
+ case SPDIF_INTCR:
+ case SPDIF_XFER:
+ case SPDIF_SMPDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_CFGR:
+ case SPDIF_SDBLR:
+ case SPDIF_INTCR:
+ case SPDIF_INTSR:
+ case SPDIF_XFER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_INTSR:
+ case SPDIF_SDBLR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rk_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIF_SMPDR,
+ .writeable_reg = rk_spdif_wr_reg,
+ .readable_reg = rk_spdif_rd_reg,
+ .volatile_reg = rk_spdif_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rk_spdif_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_spdif_dev *spdif;
+ const struct of_device_id *match;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ match = of_match_node(rk_spdif_match, np);
+ if (match->data == (void *)RK_SPDIF_RK3288) {
+ struct regmap *grf;
+
+ grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev,
+ "rockchip_spdif missing 'rockchip,grf' \n");
+ return PTR_ERR(grf);
+ }
+
+ /* Select the 8 channel SPDIF solution on RK3288 as
+ * the 2 channel one does not appear to work
+ */
+ regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16);
+ }
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+ if (!spdif)
+ return -ENOMEM;
+
+ spdif->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(spdif->hclk)) {
+ dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n");
+ return PTR_ERR(spdif->hclk);
+ }
+ ret = clk_prepare_enable(spdif->hclk);
+ if (ret) {
+ dev_err(spdif->dev, "hclock enable failed %d\n", ret);
+ return ret;
+ }
+
+ spdif->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(spdif->mclk)) {
+ dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n");
+ return PTR_ERR(spdif->mclk);
+ }
+
+ ret = clk_prepare_enable(spdif->mclk);
+ if (ret) {
+ dev_err(spdif->dev, "clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs,
+ &rk_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap)) {
+ dev_err(&pdev->dev,
+ "Failed to initialise managed register map\n");
+ return PTR_ERR(spdif->regmap);
+ }
+
+ spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR;
+ spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ spdif->playback_dma_data.maxburst = 4;
+
+ spdif->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_request_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rk_spdif_component,
+ &rk_spdif_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI\n");
+ goto err_pm_runtime;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM\n");
+ goto err_pm_runtime;
+ }
+
+ return 0;
+
+err_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int rk_spdif_remove(struct platform_device *pdev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ rk_spdif_runtime_suspend(&pdev->dev);
+
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rk_spdif_pm_ops = {
+ SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver rk_spdif_driver = {
+ .probe = rk_spdif_probe,
+ .remove = rk_spdif_remove,
+ .driver = {
+ .name = "rockchip-spdif",
+ .of_match_table = of_match_ptr(rk_spdif_match),
+ .pm = &rk_spdif_pm_ops,
+ },
+};
+module_platform_driver(rk_spdif_driver);
+
+MODULE_ALIAS("platform:rockchip-spdif");
+MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface");
+MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.co.uk>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/rockchip/rockchip_spdif.h b/kernel/sound/soc/rockchip/rockchip_spdif.h
new file mode 100644
index 000000000..3ef12770a
--- /dev/null
+++ b/kernel/sound/soc/rockchip/rockchip_spdif.h
@@ -0,0 +1,63 @@
+/*
+ * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
+ *
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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 _ROCKCHIP_SPDIF_H
+#define _ROCKCHIP_SPDIF_H
+
+/*
+ * CFGR
+ * transfer configuration register
+*/
+#define SPDIF_CFGR_CLK_DIV_SHIFT (16)
+#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT)
+#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT)
+
+#define SPDIF_CFGR_HALFWORD_SHIFT 2
+#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT)
+#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT)
+
+#define SPDIF_CFGR_VDW_SHIFT 0
+#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT)
+#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT)
+
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
+
+/*
+ * DMACR
+ * DMA control register
+*/
+#define SPDIF_DMACR_TDE_SHIFT 5
+#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT)
+#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT)
+
+#define SPDIF_DMACR_TDL_SHIFT 0
+#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT)
+
+/*
+ * XFER
+ * Transfer control register
+*/
+#define SPDIF_XFER_TXS_SHIFT 0
+#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT)
+#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT)
+
+#define SPDIF_CFGR (0x0000)
+#define SPDIF_SDBLR (0x0004)
+#define SPDIF_DMACR (0x0008)
+#define SPDIF_INTCR (0x000c)
+#define SPDIF_INTSR (0x0010)
+#define SPDIF_XFER (0x0018)
+#define SPDIF_SMPDR (0x0020)
+
+#endif /* _ROCKCHIP_SPDIF_H */
diff --git a/kernel/sound/soc/samsung/Kconfig b/kernel/sound/soc/samsung/Kconfig
index 0632a3685..3744c9ed5 100644
--- a/kernel/sound/soc/samsung/Kconfig
+++ b/kernel/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/kernel/sound/soc/samsung/arndale_rt5631.c b/kernel/sound/soc/samsung/arndale_rt5631.c
index 9e371eb3e..ee1fda92f 100644
--- a/kernel/sound/soc/samsung/arndale_rt5631.c
+++ b/kernel/sound/soc/samsung/arndale_rt5631.c
@@ -71,6 +71,7 @@ static struct snd_soc_dai_link arndale_rt5631_dai[] = {
static struct snd_soc_card arndale_rt5631 = {
.name = "Arndale RT5631",
+ .owner = THIS_MODULE,
.dai_link = arndale_rt5631_dai,
.num_links = ARRAY_SIZE(arndale_rt5631_dai),
};
diff --git a/kernel/sound/soc/samsung/h1940_uda1380.c b/kernel/sound/soc/samsung/h1940_uda1380.c
index c72e9fb26..5f5825fae 100644
--- a/kernel/sound/soc/samsung/h1940_uda1380.c
+++ b/kernel/sound/soc/samsung/h1940_uda1380.c
@@ -26,16 +26,15 @@
#include <mach/gpio-samsung.h>
#include "s3c24xx-i2s.h"
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
11025,
22050,
44100,
};
-static struct snd_pcm_hw_constraint_list hw_rates = {
+static const struct snd_pcm_hw_constraint_list hw_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
- .mask = 0,
};
static struct snd_soc_jack hp_jack;
diff --git a/kernel/sound/soc/samsung/i2s.c b/kernel/sound/soc/samsung/i2s.c
index b92ab40d2..7dbf899b2 100644
--- a/kernel/sound/soc/samsung/i2s.c
+++ b/kernel/sound/soc/samsung/i2s.c
@@ -480,10 +480,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
u32 mod, mask, val = 0;
+ unsigned long flags;
- spin_lock(i2s->lock);
+ spin_lock_irqsave(i2s->lock, flags);
mod = readl(i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
switch (clk_id) {
case SAMSUNG_I2S_OPCLK:
@@ -574,11 +575,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
return -EINVAL;
}
- spin_lock(i2s->lock);
+ spin_lock_irqsave(i2s->lock, flags);
mod = readl(i2s->addr + I2SMOD);
mod = (mod & ~mask) | val;
writel(mod, i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
return 0;
}
@@ -589,6 +590,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
struct i2s_dai *i2s = to_info(dai);
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
u32 mod, tmp = 0;
+ unsigned long flags;
lrp_shift = i2s->variant_regs->lrp_off;
sdf_shift = i2s->variant_regs->sdf_off;
@@ -648,7 +650,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- spin_lock(i2s->lock);
+ spin_lock_irqsave(i2s->lock, flags);
mod = readl(i2s->addr + I2SMOD);
/*
* Don't change the I2S mode if any controller is active on this
@@ -656,7 +658,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
*/
if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
@@ -665,7 +667,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
mod &= ~(sdf_mask | lrp_rlow | mod_slave);
mod |= tmp;
writel(mod, i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
return 0;
}
@@ -675,6 +677,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
{
struct i2s_dai *i2s = to_info(dai);
u32 mod, mask = 0, val = 0;
+ unsigned long flags;
if (!is_secondary(i2s))
mask |= (MOD_DC2_EN | MOD_DC1_EN);
@@ -743,11 +746,11 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- spin_lock(i2s->lock);
+ spin_lock_irqsave(i2s->lock, flags);
mod = readl(i2s->addr + I2SMOD);
mod = (mod & ~mask) | val;
writel(mod, i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
@@ -1493,7 +1496,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/kernel/sound/soc/samsung/lowland.c b/kernel/sound/soc/samsung/lowland.c
index 5f1560931..0d0f58208 100644
--- a/kernel/sound/soc/samsung/lowland.c
+++ b/kernel/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/kernel/sound/soc/samsung/rx1950_uda1380.c b/kernel/sound/soc/samsung/rx1950_uda1380.c
index 35e37c457..fa096abe9 100644
--- a/kernel/sound/soc/samsung/rx1950_uda1380.c
+++ b/kernel/sound/soc/samsung/rx1950_uda1380.c
@@ -38,16 +38,15 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream,
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
16000,
44100,
48000,
};
-static struct snd_pcm_hw_constraint_list hw_rates = {
+static const struct snd_pcm_hw_constraint_list hw_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
- .mask = 0,
};
static struct snd_soc_jack hp_jack;
diff --git a/kernel/sound/soc/samsung/smartq_wm8987.c b/kernel/sound/soc/samsung/smartq_wm8987.c
index dfbe2db1c..a0fe37fbe 100644
--- a/kernel/sound/soc/samsung/smartq_wm8987.c
+++ b/kernel/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/kernel/sound/soc/samsung/smdk_wm8994.c b/kernel/sound/soc/samsung/smdk_wm8994.c
index d38595fbd..ff57b192d 100644
--- a/kernel/sound/soc/samsung/smdk_wm8994.c
+++ b/kernel/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/kernel/sound/soc/samsung/snow.c b/kernel/sound/soc/samsung/snow.c
index 7651dc924..07ce2cfa4 100644
--- a/kernel/sound/soc/samsung/snow.c
+++ b/kernel/sound/soc/samsung/snow.c
@@ -56,6 +56,7 @@ static int snow_late_probe(struct snd_soc_card *card)
static struct snd_soc_card snow_snd = {
.name = "Snow-I2S",
+ .owner = THIS_MODULE,
.dai_link = snow_dai,
.num_links = ARRAY_SIZE(snow_dai),
diff --git a/kernel/sound/soc/samsung/speyside.c b/kernel/sound/soc/samsung/speyside.c
index 2dcb988bd..d1ae21c5e 100644
--- a/kernel/sound/soc/samsung/speyside.c
+++ b/kernel/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/kernel/sound/soc/sh/Kconfig b/kernel/sound/soc/sh/Kconfig
index 07114b0b0..206d1edab 100644
--- a/kernel/sound/soc/sh/Kconfig
+++ b/kernel/sound/soc/sh/Kconfig
@@ -37,10 +37,11 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on DMA_OF
+ depends on COMMON_CLK
select SND_SIMPLE_CARD
select REGMAP_MMIO
help
- This option enables R-Car SUR/SCU/SSIU/SSI sound support
+ This option enables R-Car SRU/SCU/SSIU/SSI sound support
config SND_SOC_RSRC_CARD
tristate "Renesas Sampling Rate Convert Sound Card"
diff --git a/kernel/sound/soc/sh/dma-sh7760.c b/kernel/sound/soc/sh/dma-sh7760.c
index fd11404a3..8fad4441c 100644
--- a/kernel/sound/soc/sh/dma-sh7760.c
+++ b/kernel/sound/soc/sh/dma-sh7760.c
@@ -327,13 +327,7 @@ static struct snd_soc_platform_driver sh7760_soc_platform = {
static int sh7760_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &sh7760_soc_platform);
-}
-
-static int sh7760_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev, &sh7760_soc_platform);
}
static struct platform_driver sh7760_pcm_driver = {
@@ -342,7 +336,6 @@ static struct platform_driver sh7760_pcm_driver = {
},
.probe = sh7760_soc_platform_probe,
- .remove = sh7760_soc_platform_remove,
};
module_platform_driver(sh7760_pcm_driver);
diff --git a/kernel/sound/soc/sh/fsi.c b/kernel/sound/soc/sh/fsi.c
index 142c066ea..0215c78cb 100644
--- a/kernel/sound/soc/sh/fsi.c
+++ b/kernel/sound/soc/sh/fsi.c
@@ -1911,7 +1911,6 @@ MODULE_DEVICE_TABLE(of, fsi_of_match);
static const struct platform_device_id fsi_id_table[] = {
{ "sh_fsi", (kernel_ulong_t)&fsi1_core },
- { "sh_fsi2", (kernel_ulong_t)&fsi2_core },
{},
};
MODULE_DEVICE_TABLE(platform, fsi_id_table);
diff --git a/kernel/sound/soc/sh/migor.c b/kernel/sound/soc/sh/migor.c
index 82f582344..672bcd4c2 100644
--- a/kernel/sound/soc/sh/migor.c
+++ b/kernel/sound/soc/sh/migor.c
@@ -162,12 +162,11 @@ static int __init migor_init(void)
if (ret < 0)
return ret;
- siumckb_lookup = clkdev_alloc(&siumckb_clk, "siumckb_clk", NULL);
+ siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL);
if (!siumckb_lookup) {
ret = -ENOMEM;
goto eclkdevalloc;
}
- clkdev_add(siumckb_lookup);
/* Port number used on this machine: port B */
migor_snd_device = platform_device_alloc("soc-audio", 1);
diff --git a/kernel/sound/soc/sh/rcar/Makefile b/kernel/sound/soc/sh/rcar/Makefile
index f1b445173..8b258501a 100644
--- a/kernel/sound/soc/sh/rcar/Makefile
+++ b/kernel/sound/soc/sh/rcar/Makefile
@@ -1,4 +1,4 @@
-snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o
+snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
snd-soc-rsrc-card-objs := rsrc-card.o
diff --git a/kernel/sound/soc/sh/rcar/adg.c b/kernel/sound/soc/sh/rcar/adg.c
index fefc881db..2a5b3a293 100644
--- a/kernel/sound/soc/sh/rcar/adg.c
+++ b/kernel/sound/soc/sh/rcar/adg.c
@@ -7,7 +7,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
-#include <linux/sh_clk.h>
+#include <linux/clk-provider.h>
#include "rsnd.h"
#define CLKA 0
@@ -16,12 +16,26 @@
#define CLKI 3
#define CLKMAX 4
+#define CLKOUT 0
+#define CLKOUT1 1
+#define CLKOUT2 2
+#define CLKOUT3 3
+#define CLKOUTMAX 4
+
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+ .name = "adg",
+};
+
struct rsnd_adg {
struct clk *clk[CLKMAX];
+ struct clk *clkout[CLKOUTMAX];
+ struct clk_onecell_data onecell;
+ struct rsnd_mod mod;
- int rbga_rate_for_441khz_div_6; /* RBGA */
- int rbgb_rate_for_48khz_div_6; /* RBGB */
- u32 ckr;
+ int rbga_rate_for_441khz; /* RBGA */
+ int rbgb_rate_for_48khz; /* RBGB */
};
#define for_each_rsnd_clk(pos, adg, i) \
@@ -29,17 +43,36 @@ struct rsnd_adg {
(i < CLKMAX) && \
((pos) = adg->clk[i]); \
i++)
+#define for_each_rsnd_clkout(pos, adg, i) \
+ for (i = 0; \
+ (i < CLKOUTMAX) && \
+ ((pos) = adg->clkout[i]); \
+ i++)
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+{
+ int i, ratio;
+
+ if (!div)
+ return 0;
+
+ for (i = 3; i >= 0; i--) {
+ ratio = 2 << (i * 2);
+ if (0 == (div % ratio))
+ return (u32)((i << 8) | ((div / ratio) - 1));
+ }
+
+ return ~0;
+}
static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
int id = rsnd_mod_id(mod);
int ws = id;
- if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) {
+ if (rsnd_ssi_is_pin_sharing(io)) {
switch (id) {
case 1:
case 2:
@@ -60,6 +93,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int id = rsnd_mod_id(mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, val;
@@ -69,21 +105,26 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
val = val << shift;
mask = 0xffff << shift;
- rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+ rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
return 0;
}
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
u32 timsel)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int is_play = rsnd_io_is_play(io);
- int id = rsnd_mod_id(mod);
+ int id = rsnd_mod_id(src_mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, ws;
u32 in, out;
+ rsnd_mod_confirm_src(src_mod);
+
ws = rsnd_adg_ssi_ws_timing_gen2(io);
in = (is_play) ? timsel : ws;
@@ -95,37 +136,38 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
switch (id / 2) {
case 0:
- rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
break;
case 1:
- rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
break;
case 2:
- rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
break;
case 3:
- rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
break;
case 4:
- rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
break;
}
return 0;
}
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
unsigned int src_rate,
unsigned int dst_rate)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, step, ret;
u32 val, en;
@@ -134,10 +176,12 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
- adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
- adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */
+ adg->rbga_rate_for_441khz, /* 0011: RBGA */
+ adg->rbgb_rate_for_48khz, /* 0100: RBGB */
};
+ rsnd_mod_confirm_src(src_mod);
+
min = ~0;
val = 0;
en = 0;
@@ -175,25 +219,27 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
return -EIO;
}
- ret = rsnd_adg_set_src_timsel_gen2(mod, io, val);
+ ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
if (ret < 0) {
dev_err(dev, "timsel error\n");
return ret;
}
- rsnd_mod_bset(mod, DIV_EN, en, en);
+ rsnd_mod_bset(adg_mod, DIV_EN, en, en);
dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
return 0;
}
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io)
{
u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
- return rsnd_adg_set_src_timsel_gen2(mod, io, val);
+ rsnd_mod_confirm_src(src_mod);
+
+ return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
}
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
@@ -202,6 +248,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
unsigned int dst_rate)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, shift;
u32 mask, val;
@@ -211,8 +258,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */
0, /* 011: MLBCLK (not used) */
- adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
- adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */
+ adg->rbga_rate_for_441khz, /* 100: RBGA */
+ adg->rbgb_rate_for_48khz, /* 101: RBGB */
};
/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
@@ -238,13 +285,13 @@ find_rate:
switch (id / 4) {
case 0:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
break;
case 1:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
break;
case 2:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
break;
}
@@ -257,12 +304,17 @@ find_rate:
return 0;
}
-static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{
- int id = rsnd_mod_id(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ int id = rsnd_mod_id(ssi_mod);
int shift = (id % 4) * 8;
u32 mask = 0xFF << shift;
+ rsnd_mod_confirm_ssi(ssi_mod);
+
val = val << shift;
/*
@@ -274,13 +326,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
switch (id / 4) {
case 0:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
break;
case 1:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
break;
case 2:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
break;
}
}
@@ -326,14 +378,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
}
/*
- * find 1/6 clock from BRGA/BRGB
+ * find divided clock from BRGA/BRGB
*/
- if (rate == adg->rbga_rate_for_441khz_div_6) {
+ if (rate == adg->rbga_rate_for_441khz) {
data = 0x10;
goto found_clock;
}
- if (rate == adg->rbgb_rate_for_48khz_div_6) {
+ if (rate == adg->rbgb_rate_for_48khz) {
data = 0x20;
goto found_clock;
}
@@ -342,29 +394,60 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
found_clock:
- /* see rsnd_adg_ssi_clk_init() */
- rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr);
- rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */
- rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */
-
/*
* This "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, data);
- dev_dbg(dev, "ADG: ssi%d selects clk%d = %d",
- rsnd_mod_id(mod), i, rate);
+ dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod),
+ data, rate);
return 0;
}
-static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
+ struct rsnd_adg *adg)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ static const char * const clk_name[] = {
+ [CLKA] = "clk_a",
+ [CLKB] = "clk_b",
+ [CLKC] = "clk_c",
+ [CLKI] = "clk_i",
+ };
+ int i;
+
+ for (i = 0; i < CLKMAX; i++) {
+ clk = devm_clk_get(dev, clk_name[i]);
+ adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+ }
+
+ for_each_rsnd_clk(clk, adg, i)
+ dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+}
+
+static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
+ struct rsnd_adg *adg)
{
struct clk *clk;
- unsigned long rate;
- u32 ckr;
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+ u32 ckr, rbgx, rbga, rbgb;
+ u32 rate, req_rate, div;
+ uint32_t count = 0;
+ unsigned long req_48kHz_rate, req_441kHz_rate;
int i;
+ const char *parent_clk_name = NULL;
+ static const char * const clkout_name[] = {
+ [CLKOUT] = "audio_clkout",
+ [CLKOUT1] = "audio_clkout1",
+ [CLKOUT2] = "audio_clkout2",
+ [CLKOUT3] = "audio_clkout3",
+ };
int brg_table[] = {
[CLKA] = 0x0,
[CLKB] = 0x1,
@@ -372,19 +455,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
[CLKI] = 0x2,
};
+ of_property_read_u32(np, "#clock-cells", &count);
+
+ /*
+ * ADG supports BRRA/BRRB output only
+ * this means all clkout0/1/2/3 will be same rate
+ */
+ of_property_read_u32(np, "clock-frequency", &req_rate);
+ req_48kHz_rate = 0;
+ req_441kHz_rate = 0;
+ if (0 == (req_rate % 44100))
+ req_441kHz_rate = req_rate;
+ if (0 == (req_rate % 48000))
+ req_48kHz_rate = req_rate;
+
/*
* This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
* have 44.1kHz or 48kHz base clocks for now.
*
* SSI itself can divide parent clock by 1/1 - 1/16
- * So, BRGA outputs 44.1kHz base parent clock 1/32,
- * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
* see
* rsnd_adg_ssi_clk_try_start()
+ * rsnd_ssi_master_clk_start()
*/
ckr = 0;
- adg->rbga_rate_for_441khz_div_6 = 0;
- adg->rbgb_rate_for_48khz_div_6 = 0;
+ rbga = 2; /* default 1/6 */
+ rbgb = 2; /* default 1/6 */
+ adg->rbga_rate_for_441khz = 0;
+ adg->rbgb_rate_for_48khz = 0;
for_each_rsnd_clk(clk, adg, i) {
rate = clk_get_rate(clk);
@@ -392,19 +490,86 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
continue;
/* RBGA */
- if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) {
- adg->rbga_rate_for_441khz_div_6 = rate / 6;
- ckr |= brg_table[i] << 20;
+ if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+ div = 6;
+ if (req_441kHz_rate)
+ div = rate / req_441kHz_rate;
+ rbgx = rsnd_adg_calculate_rbgx(div);
+ if (BRRx_MASK(rbgx) == rbgx) {
+ rbga = rbgx;
+ adg->rbga_rate_for_441khz = rate / div;
+ ckr |= brg_table[i] << 20;
+ if (req_441kHz_rate)
+ parent_clk_name = __clk_get_name(clk);
+ }
}
/* RBGB */
- if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) {
- adg->rbgb_rate_for_48khz_div_6 = rate / 6;
- ckr |= brg_table[i] << 16;
+ if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+ div = 6;
+ if (req_48kHz_rate)
+ div = rate / req_48kHz_rate;
+ rbgx = rsnd_adg_calculate_rbgx(div);
+ if (BRRx_MASK(rbgx) == rbgx) {
+ rbgb = rbgx;
+ adg->rbgb_rate_for_48khz = rate / div;
+ ckr |= brg_table[i] << 16;
+ if (req_48kHz_rate) {
+ parent_clk_name = __clk_get_name(clk);
+ ckr |= 0x80000000;
+ }
+ }
}
}
- adg->ckr = ckr;
+ /*
+ * ADG supports BRRA/BRRB output only.
+ * this means all clkout0/1/2/3 will be * same rate
+ */
+
+ /*
+ * for clkout
+ */
+ if (!count) {
+ clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+ parent_clk_name,
+ (parent_clk_name) ?
+ 0 : CLK_IS_ROOT, req_rate);
+ if (!IS_ERR(clk)) {
+ adg->clkout[CLKOUT] = clk;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ }
+ }
+ /*
+ * for clkout0/1/2/3
+ */
+ else {
+ for (i = 0; i < CLKOUTMAX; i++) {
+ clk = clk_register_fixed_rate(dev, clkout_name[i],
+ parent_clk_name,
+ (parent_clk_name) ?
+ 0 : CLK_IS_ROOT,
+ req_rate);
+ if (!IS_ERR(clk)) {
+ adg->onecell.clks = adg->clkout;
+ adg->onecell.clk_num = CLKOUTMAX;
+
+ adg->clkout[i] = clk;
+
+ of_clk_add_provider(np, of_clk_src_onecell_get,
+ &adg->onecell);
+ }
+ }
+ }
+
+ rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+ rsnd_mod_write(adg_mod, BRRA, rbga);
+ rsnd_mod_write(adg_mod, BRRB, rbgb);
+
+ for_each_rsnd_clkout(clk, adg, i)
+ dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+ dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+ ckr, rbga, rbgb);
}
int rsnd_adg_probe(struct platform_device *pdev,
@@ -413,8 +578,6 @@ int rsnd_adg_probe(struct platform_device *pdev,
{
struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv);
- struct clk *clk;
- int i;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
if (!adg) {
@@ -422,15 +585,16 @@ int rsnd_adg_probe(struct platform_device *pdev,
return -ENOMEM;
}
- adg->clk[CLKA] = devm_clk_get(dev, "clk_a");
- adg->clk[CLKB] = devm_clk_get(dev, "clk_b");
- adg->clk[CLKC] = devm_clk_get(dev, "clk_c");
- adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
-
- for_each_rsnd_clk(clk, adg, i)
- dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+ /*
+ * ADG is special module.
+ * Use ADG mod without rsnd_mod_init() to make debug easy
+ * for rsnd_write/rsnd_read
+ */
+ adg->mod.ops = &adg_ops;
+ adg->mod.priv = priv;
- rsnd_adg_ssi_clk_init(priv, adg);
+ rsnd_adg_get_clkin(priv, adg);
+ rsnd_adg_get_clkout(priv, adg);
priv->adg = adg;
diff --git a/kernel/sound/soc/sh/rcar/core.c b/kernel/sound/soc/sh/rcar/core.c
index 9f48d75fa..deed48ef2 100644
--- a/kernel/sound/soc/sh/rcar/core.c
+++ b/kernel/sound/soc/sh/rcar/core.c
@@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = {
static const struct of_device_id rsnd_of_match[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
+ { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
{},
};
MODULE_DEVICE_TABLE(of, rsnd_of_match);
@@ -126,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
#define rsnd_info_id(priv, io, name) \
((io)->info->name - priv->info->name##_info)
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+ if (mod->type != type) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "%s[%d] is not your expected module\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+ }
+}
+
/*
* rsnd_mod functions
*/
@@ -137,15 +149,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 +174,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 +185,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
+ * ADINR function
*/
-u32 rsnd_get_adinr(struct rsnd_mod *mod)
+u32 rsnd_get_adinr_bit(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;
@@ -196,30 +239,86 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
return adinr;
}
+u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 chan = runtime->channels;
+
+ switch (chan) {
+ case 1:
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ break;
+ default:
+ dev_warn(dev, "not supported channel\n");
+ chan = 0;
+ break;
+ }
+
+ return chan;
+}
+
+/*
+ * DALIGN function
+ */
+u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+ struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *target = src ? src : ssi;
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ u32 val = 0x76543210;
+ u32 mask = ~0;
+
+ mask <<= runtime->channels * 4;
+ val = val & mask;
+
+ switch (runtime->sample_bits) {
+ case 16:
+ val |= 0x67452301 & ~mask;
+ break;
+ case 32:
+ val |= 0x76543210 & ~mask;
+ break;
+ }
+
+ /*
+ * exchange channeles on SRC if possible,
+ * otherwise, R/L volume settings on DVC
+ * changes inverted channels
+ */
+ if (mod == target)
+ return val;
+ else
+ return 0x76543210;
+}
+
/*
* rsnd_dai functions
*/
-#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 call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
+ mod->status = (mod->status & ~mask) + \
+ (add << __rsnd_mod_shift_##func); \
+ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), \
+ mod->status, call ? #func : ""); \
+ if (call) \
+ ret = (mod)->ops->func(mod, io, param); \
ret; \
})
-#define rsnd_mod_call(mod, func, param...) \
- (!(mod) ? -ENODEV : \
- !((mod)->ops->func) ? 0 : \
- __rsnd_mod_call(mod, func, param))
-
#define rsnd_dai_call(fn, io, param...) \
({ \
struct rsnd_mod *mod; \
@@ -228,9 +327,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
mod = (io)->mod[i]; \
if (!mod) \
continue; \
- ret = rsnd_mod_call(mod, fn, param); \
- if (ret < 0) \
- break; \
+ ret |= rsnd_mod_call(mod, io, fn, param); \
} \
ret; \
})
@@ -238,21 +335,20 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
static int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
+ struct rsnd_priv *priv;
+ struct device *dev;
+
if (!mod)
return -EIO;
- if (io->mod[mod->type]) {
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- dev_err(dev, "%s[%d] is not empty\n",
- rsnd_mod_name(mod),
- rsnd_mod_id(mod));
- return -EIO;
- }
+ priv = rsnd_mod_to_priv(mod);
+ dev = rsnd_priv_to_dev(priv);
io->mod[mod->type] = mod;
- mod->io = io;
+
+ dev_dbg(dev, "%s[%d] is connected to io (%s)\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod),
+ rsnd_io_is_play(io) ? "Playback" : "Capture");
return 0;
}
@@ -260,7 +356,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 +367,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 +389,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 +406,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 +438,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 +466,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)
@@ -380,23 +493,19 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
break;
case SNDRV_PCM_TRIGGER_STOP:
ret = rsnd_dai_call(stop, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
- ret = rsnd_dai_call(quit, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
+ ret |= rsnd_dai_call(quit, io, priv);
- ret = rsnd_platform_call(priv, dai, stop, ssi_id);
- if (ret < 0)
- goto dai_trigger_end;
+ ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
+
+ rsnd_dai_stream_quit(io);
break;
default:
ret = -EINVAL;
}
dai_trigger_end:
- rsnd_unlock(priv, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
@@ -463,7 +572,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
.set_fmt = rsnd_soc_dai_set_fmt,
};
-#define rsnd_path_parse(priv, io, type) \
+#define rsnd_path_add(priv, io, type) \
({ \
struct rsnd_mod *mod; \
int ret = 0; \
@@ -479,7 +588,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
ret; \
})
-#define rsnd_path_break(priv, io, type) \
+#define rsnd_path_remove(priv, io, type) \
{ \
struct rsnd_mod *mod; \
int id = -1; \
@@ -493,6 +602,79 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
} \
}
+void rsnd_path_parse(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+ struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+ struct rsnd_mod *cmd;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 data;
+
+ /* Gen1 is not supported */
+ if (rsnd_is_gen1(priv))
+ return;
+
+ if (!mix && !dvc)
+ return;
+
+ if (mix) {
+ struct rsnd_dai *rdai;
+ int i;
+ u32 path[] = {
+ [0] = 0,
+ [1] = 1 << 0,
+ [2] = 0,
+ [3] = 0,
+ [4] = 0,
+ [5] = 1 << 8
+ };
+
+ /*
+ * it is assuming that integrater is well understanding about
+ * data path. Here doesn't check impossible connection,
+ * like src2 + src5
+ */
+ data = 0;
+ for_each_rsnd_dai(rdai, priv, i) {
+ io = &rdai->playback;
+ if (mix == rsnd_io_to_mod_mix(io))
+ data |= path[rsnd_mod_id(src)];
+
+ io = &rdai->capture;
+ if (mix == rsnd_io_to_mod_mix(io))
+ data |= path[rsnd_mod_id(src)];
+ }
+
+ /*
+ * We can't use ctu = rsnd_io_ctu() here.
+ * Since, ID of dvc/mix are 0 or 1 (= same as CMD number)
+ * but ctu IDs are 0 - 7 (= CTU00 - CTU13)
+ */
+ cmd = mix;
+ } else {
+ u32 path[] = {
+ [0] = 0x30000,
+ [1] = 0x30001,
+ [2] = 0x40000,
+ [3] = 0x10000,
+ [4] = 0x20000,
+ [5] = 0x40100
+ };
+
+ data = path[rsnd_mod_id(src)];
+
+ cmd = dvc;
+ }
+
+ dev_dbg(dev, "ctu/mix path = 0x%08x", data);
+
+ rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
+
+ rsnd_mod_write(cmd, CMD_CTRL, 0x10);
+}
+
static int rsnd_path_init(struct rsnd_priv *priv,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
@@ -510,18 +692,28 @@ static int rsnd_path_init(struct rsnd_priv *priv,
* using fixed path.
*/
+ /* SSI */
+ ret = rsnd_path_add(priv, io, ssi);
+ if (ret < 0)
+ return ret;
+
/* SRC */
- ret = rsnd_path_parse(priv, io, src);
+ ret = rsnd_path_add(priv, io, src);
if (ret < 0)
return ret;
- /* SSI */
- ret = rsnd_path_parse(priv, io, ssi);
+ /* CTU */
+ ret = rsnd_path_add(priv, io, ctu);
+ if (ret < 0)
+ return ret;
+
+ /* MIX */
+ ret = rsnd_path_add(priv, io, mix);
if (ret < 0)
return ret;
/* DVC */
- ret = rsnd_path_parse(priv, io, dvc);
+ ret = rsnd_path_add(priv, io, dvc);
if (ret < 0)
return ret;
@@ -535,13 +727,15 @@ static void rsnd_of_parse_dai(struct platform_device *pdev,
struct device_node *dai_node, *dai_np;
struct device_node *ssi_node, *ssi_np;
struct device_node *src_node, *src_np;
+ struct device_node *ctu_node, *ctu_np;
+ struct device_node *mix_node, *mix_np;
struct device_node *dvc_node, *dvc_np;
struct device_node *playback, *capture;
struct rsnd_dai_platform_info *dai_info;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr, i;
- int dai_i, ssi_i, src_i, dvc_i;
+ int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i;
if (!of_data)
return;
@@ -567,6 +761,8 @@ static void rsnd_of_parse_dai(struct platform_device *pdev,
ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
+ ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
+ mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
#define mod_parse(name) \
@@ -603,6 +799,8 @@ if (name##_node) { \
mod_parse(ssi);
mod_parse(src);
+ mod_parse(ctu);
+ mod_parse(mix);
mod_parse(dvc);
of_node_put(playback);
@@ -822,23 +1020,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 +1060,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 +1071,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 +1114,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);
}
/*
@@ -968,8 +1177,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
/*
* remove SRC/DVC from DAI,
*/
- rsnd_path_break(priv, io, src);
- rsnd_path_break(priv, io, dvc);
+ rsnd_path_remove(priv, io, src);
+ rsnd_path_remove(priv, io, dvc);
/*
* fallback
@@ -1004,26 +1213,19 @@ static int rsnd_probe(struct platform_device *pdev)
rsnd_dma_probe,
rsnd_ssi_probe,
rsnd_src_probe,
+ rsnd_ctu_probe,
+ rsnd_mix_probe,
rsnd_dvc_probe,
rsnd_adg_probe,
rsnd_dai_probe,
};
int ret, i;
- info = NULL;
- of_data = NULL;
- if (of_id) {
- info = devm_kzalloc(&pdev->dev,
- sizeof(struct rcar_snd_info), GFP_KERNEL);
- of_data = of_id->data;
- } else {
- info = pdev->dev.platform_data;
- }
-
- if (!info) {
- dev_err(dev, "driver needs R-Car sound information\n");
- return -ENODEV;
- }
+ info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ of_data = of_id->data;
/*
* init priv data
@@ -1099,6 +1301,8 @@ static int rsnd_remove(struct platform_device *pdev)
struct rsnd_priv *priv) = {
rsnd_ssi_remove,
rsnd_src_remove,
+ rsnd_ctu_remove,
+ rsnd_mix_remove,
rsnd_dvc_remove,
};
int ret = 0, i;
diff --git a/kernel/sound/soc/sh/rcar/ctu.c b/kernel/sound/soc/sh/rcar/ctu.c
new file mode 100644
index 000000000..3cb214ab8
--- /dev/null
+++ b/kernel/sound/soc/sh/rcar/ctu.c
@@ -0,0 +1,169 @@
+/*
+ * ctu.c
+ *
+ * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "rsnd.h"
+
+#define CTU_NAME_SIZE 16
+#define CTU_NAME "ctu"
+
+struct rsnd_ctu {
+ struct rsnd_ctu_platform_info *info; /* rcar_snd.h */
+ struct rsnd_mod mod;
+};
+
+#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
+#define for_each_rsnd_ctu(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_ctu_nr(priv)) && \
+ ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
+ i++)
+
+#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
+#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
+static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
+{
+ rsnd_mod_write(mod, CTU_CTUIR, enable);
+}
+
+static int rsnd_ctu_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_power_on(mod);
+
+ rsnd_ctu_initialize_lock(mod);
+
+ rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
+
+ rsnd_ctu_initialize_unlock(mod);
+
+ return 0;
+}
+
+static int rsnd_ctu_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_power_off(mod);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ctu_ops = {
+ .name = CTU_NAME,
+ .init = rsnd_ctu_init,
+ .quit = rsnd_ctu_quit,
+};
+
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
+}
+
+static void rsnd_of_parse_ctu(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct rsnd_ctu_platform_info *ctu_info;
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = &pdev->dev;
+ int nr;
+
+ if (!of_data)
+ return;
+
+ node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
+ if (!node)
+ return;
+
+ nr = of_get_child_count(node);
+ if (!nr)
+ goto rsnd_of_parse_ctu_end;
+
+ ctu_info = devm_kzalloc(dev,
+ sizeof(struct rsnd_ctu_platform_info) * nr,
+ GFP_KERNEL);
+ if (!ctu_info) {
+ dev_err(dev, "ctu info allocation error\n");
+ goto rsnd_of_parse_ctu_end;
+ }
+
+ info->ctu_info = ctu_info;
+ info->ctu_info_nr = nr;
+
+rsnd_of_parse_ctu_end:
+ of_node_put(node);
+
+}
+
+int rsnd_ctu_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_ctu *ctu;
+ struct clk *clk;
+ char name[CTU_NAME_SIZE];
+ int i, nr, ret;
+
+ /* This driver doesn't support Gen1 at this point */
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ rsnd_of_parse_ctu(pdev, of_data, priv);
+
+ nr = info->ctu_info_nr;
+ if (!nr)
+ return 0;
+
+ ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
+ if (!ctu)
+ return -ENOMEM;
+
+ priv->ctu_nr = nr;
+ priv->ctu = ctu;
+
+ for_each_rsnd_ctu(ctu, priv, i) {
+ /*
+ * CTU00, CTU01, CTU02, CTU03 => CTU0
+ * CTU10, CTU11, CTU12, CTU13 => CTU1
+ */
+ snprintf(name, CTU_NAME_SIZE, "%s.%d",
+ CTU_NAME, i / 4);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ctu->info = &info->ctu_info[i];
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
+ clk, RSND_MOD_CTU, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void rsnd_ctu_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ctu *ctu;
+ int i;
+
+ for_each_rsnd_ctu(ctu, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(ctu));
+ }
+}
diff --git a/kernel/sound/soc/sh/rcar/dma.c b/kernel/sound/soc/sh/rcar/dma.c
index 144308f15..5d084d040 100644
--- a/kernel/sound/soc/sh/rcar/dma.c
+++ b/kernel/sound/soc/sh/rcar/dma.c
@@ -27,16 +27,26 @@ struct rsnd_dma_ctrl {
int dmapp_num;
};
+struct rsnd_dma_ops {
+ char *name;
+ void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
+ void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
+ int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
+ struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
+ void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
+};
+
#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma)
/*
* 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 +59,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);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (elapsed)
+ rsnd_dai_period_elapsed(io);
+}
+
+static void rsnd_dmaen_complete(void *data)
+{
+ struct rsnd_mod *mod = data;
- rsnd_dai_pointer_update(io, io->byte_per_period);
+ rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
}
-static void rsnd_dmaen_stop(struct rsnd_dma *dma)
+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 +107,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 +138,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 +147,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 +169,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;
@@ -153,7 +177,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
dma_cap_set(DMA_SLAVE, mask);
dmaen->chan = dma_request_channel(mask, shdma_chan_filter,
- (void *)id);
+ (void *)(uintptr_t)id);
}
if (IS_ERR_OR_NULL(dmaen->chan)) {
dmaen->chan = NULL;
@@ -167,7 +191,8 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- dev_dbg(dev, "dma : %pad -> %pad\n",
+ dev_dbg(dev, "%s %pad -> %pad\n",
+ dma->ops->name,
&cfg.src_addr, &cfg.dst_addr);
ret = dmaengine_slave_config(dmaen->chan, &cfg);
@@ -177,7 +202,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 +214,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);
@@ -200,6 +225,7 @@ static void rsnd_dmaen_quit(struct rsnd_dma *dma)
}
static struct rsnd_dma_ops rsnd_dmaen_ops = {
+ .name = "audmac",
.start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop,
.init = rsnd_dmaen_init,
@@ -238,9 +264,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 +294,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 +326,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 +339,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 +348,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);
@@ -342,6 +371,7 @@ static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
}
static struct rsnd_dma_ops rsnd_dmapp_ops = {
+ .name = "audmac-pp",
.start = rsnd_dmapp_start,
.stop = rsnd_dmapp_stop,
.init = rsnd_dmapp_init,
@@ -386,17 +416,19 @@ 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);
int use_src = !!rsnd_io_to_mod_src(io);
- int use_dvc = !!rsnd_io_to_mod_dvc(io);
+ int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
+ !!rsnd_io_to_mod_mix(io) ||
+ !!rsnd_io_to_mod_ctu(io);
int id = rsnd_mod_id(mod);
struct dma_addr {
dma_addr_t out_addr;
@@ -434,22 +466,24 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv,
};
/* it shouldn't happen */
- if (use_dvc && !use_src)
+ if (use_cmd && !use_src)
dev_err(dev, "DVC is selected without SRC\n");
/* use SSIU or SSI ? */
- if (is_ssi && rsnd_ssi_use_busif(mod))
+ if (is_ssi && rsnd_ssi_use_busif(io))
is_ssi++;
return (is_from) ?
- dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
- dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
+ dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr :
+ dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr;
}
-static dma_addr_t rsnd_dma_addr(struct rsnd_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,82 +493,108 @@ 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 */
+#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
static void rsnd_dma_of_path(struct rsnd_dma *dma,
+ struct rsnd_dai_stream *io,
int is_play,
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 *ctu = rsnd_io_to_mod_ctu(io);
+ struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mod[MOD_MAX];
- int i, index;
+ struct rsnd_mod *mod_start, *mod_end;
+ struct rsnd_priv *priv = rsnd_mod_to_priv(this);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int nr, i;
+ if (!ssi)
+ return;
- for (i = 0; i < MOD_MAX; i++)
+ nr = 0;
+ for (i = 0; i < MOD_MAX; i++) {
mod[i] = NULL;
+ nr += !!rsnd_io_to_mod(io, i);
+ }
/*
- * in play case...
+ * [S] -*-> [E]
+ * [S] -*-> SRC -o-> [E]
+ * [S] -*-> SRC -> DVC -o-> [E]
+ * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E]
+ *
+ * playback [S] = mem
+ * [E] = SSI
*
- * src -> dst
+ * capture [S] = SSI
+ * [E] = mem
*
- * mem -> SSI
- * mem -> SRC -> SSI
- * mem -> SRC -> DVC -> SSI
+ * -*-> Audio DMAC
+ * -o-> Audio DMAC peri peri
*/
- mod[0] = NULL; /* for "mem" */
- index = 1;
- for (i = 1; i < MOD_MAX; i++) {
- if (!src) {
- mod[i] = ssi;
- } else if (!dvc) {
- mod[i] = src;
- src = NULL;
- } else {
- if ((!is_play) && (this == src))
- this = dvc;
+ mod_start = (is_play) ? NULL : ssi;
+ mod_end = (is_play) ? ssi : NULL;
- mod[i] = (is_play) ? src : dvc;
- i++;
- mod[i] = (is_play) ? dvc : src;
+ mod[0] = mod_start;
+ for (i = 1; i < nr; i++) {
+ if (src) {
+ mod[i] = src;
src = NULL;
+ } else if (ctu) {
+ mod[i] = ctu;
+ ctu = NULL;
+ } else if (mix) {
+ mod[i] = mix;
+ mix = NULL;
+ } else if (dvc) {
+ mod[i] = dvc;
dvc = NULL;
}
-
- if (mod[i] == this)
- index = i;
-
- if (mod[i] == ssi)
- break;
}
+ mod[i] = mod_end;
- if (is_play) {
- *mod_from = mod[index - 1];
- *mod_to = mod[index];
+ /*
+ * | SSI | SRC |
+ * -------------+-----+-----+
+ * is_play | o | * |
+ * !is_play | * | o |
+ */
+ if ((this == ssi) == (is_play)) {
+ *mod_from = mod[nr - 1];
+ *mod_to = mod[nr];
} else {
- *mod_from = mod[index];
- *mod_to = mod[index - 1];
+ *mod_from = mod[0];
+ *mod_to = mod[1];
+ }
+
+ dev_dbg(dev, "module connection (this is %s[%d])\n",
+ rsnd_mod_name(this), rsnd_mod_id(this));
+ for (i = 0; i <= nr; i++) {
+ dev_dbg(dev, " %s[%d]%s\n",
+ rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]),
+ (mod[i] == *mod_from) ? " from" :
+ (mod[i] == *mod_to) ? " to" : "");
}
}
-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,16 +603,16 @@ 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_mod *mod_from = NULL;
+ struct rsnd_mod *mod_to = NULL;
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
int is_play = rsnd_io_is_play(io);
/*
@@ -564,10 +624,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 +639,12 @@ 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);
+ dev_dbg(dev, "%s %s[%d] -> %s[%d]\n",
+ dma->ops->name,
+ rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
+ rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
+
+ return dma->ops->init(io, dma, id, mod_from, mod_to);
}
int rsnd_dma_probe(struct platform_device *pdev,
diff --git a/kernel/sound/soc/sh/rcar/dvc.c b/kernel/sound/soc/sh/rcar/dvc.c
index e5fcb062a..58f690900 100644
--- a/kernel/sound/soc/sh/rcar/dvc.c
+++ b/kernel/sound/soc/sh/rcar/dvc.c
@@ -24,6 +24,7 @@ struct rsnd_dvc {
struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */
};
+#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
#define rsnd_dvc_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
@@ -63,7 +64,21 @@ 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_soft_reset(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, DVC_SWRSR, 0);
+ rsnd_mod_write(mod, DVC_SWRSR, 1);
+}
+
+#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1)
+#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0)
+static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable)
+{
+ rsnd_mod_write(mod, DVC_DVUIR, enable);
+}
+
+static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 val[RSND_DVC_CHANNELS];
@@ -120,6 +135,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);
@@ -133,70 +149,50 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
return 0;
}
-static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
+static int rsnd_dvc_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- struct rsnd_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);
- int src_id = rsnd_mod_id(src_mod);
- u32 route[] = {
- [0] = 0x30000,
- [1] = 0x30001,
- [2] = 0x40000,
- [3] = 0x10000,
- [4] = 0x20000,
- [5] = 0x40100
- };
-
- if (src_id >= ARRAY_SIZE(route)) {
- dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id);
- return -EINVAL;
- }
-
- rsnd_mod_hw_start(dvc_mod);
+ rsnd_mod_power_on(mod);
- /*
- * fixme
- * it doesn't support CTU/MIX
- */
- rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]);
+ rsnd_dvc_soft_reset(mod);
- rsnd_mod_write(dvc_mod, DVC_SWRSR, 0);
- rsnd_mod_write(dvc_mod, DVC_SWRSR, 1);
+ rsnd_dvc_initialize_lock(mod);
- rsnd_mod_write(dvc_mod, DVC_DVUIR, 1);
+ rsnd_path_parse(priv, io);
- rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
+ rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io));
/* ch0/ch1 Volume */
- rsnd_dvc_volume_update(dvc_mod);
+ rsnd_dvc_volume_update(io, mod);
- rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
-
- rsnd_adg_set_cmd_timsel_gen2(dvc_mod, io);
+ rsnd_adg_set_cmd_timsel_gen2(mod, io);
return 0;
}
static int rsnd_dvc_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
static int rsnd_dvc_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
+ rsnd_dvc_initialize_unlock(mod);
+
rsnd_mod_write(mod, CMD_CTRL, 0x10);
return 0;
}
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 +201,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 +218,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 +227,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 +235,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 +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 Down Rate" : "DVC In Ramp Down Rate",
&dvc->rdown,
@@ -261,7 +257,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);
@@ -285,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
id = 0;
- return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
+ return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
}
static void rsnd_of_parse_dvc(struct platform_device *pdev,
@@ -335,23 +332,19 @@ int rsnd_dvc_probe(struct platform_device *pdev,
char name[RSND_DVC_NAME_SIZE];
int i, nr, ret;
+ /* This driver doesn't support Gen1 at this point */
+ if (rsnd_is_gen1(priv))
+ return 0;
+
rsnd_of_parse_dvc(pdev, of_data, priv);
nr = info->dvc_info_nr;
if (!nr)
return 0;
- /* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "CMD is not supported on Gen1\n");
- return -EINVAL;
- }
-
dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
- if (!dvc) {
- dev_err(dev, "CMD allocate failed\n");
+ if (!dvc)
return -ENOMEM;
- }
priv->dvc_nr = nr;
priv->dvc = dvc;
@@ -366,7 +359,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, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
if (ret)
return ret;
@@ -382,6 +375,6 @@ void rsnd_dvc_remove(struct platform_device *pdev,
int i;
for_each_rsnd_dvc(dvc, priv, i) {
- rsnd_mod_quit(&dvc->mod);
+ rsnd_mod_quit(rsnd_mod_get(dvc));
}
}
diff --git a/kernel/sound/soc/sh/rcar/gen.c b/kernel/sound/soc/sh/rcar/gen.c
index 8c7dc51b1..edcf4cc2e 100644
--- a/kernel/sound/soc/sh/rcar/gen.c
+++ b/kernel/sound/soc/sh/rcar/gen.c
@@ -22,13 +22,15 @@
#include "rsnd.h"
struct rsnd_gen {
- void __iomem *base[RSND_BASE_MAX];
-
struct rsnd_gen_ops *ops;
+ /* RSND_BASE_MAX base */
+ void __iomem *base[RSND_BASE_MAX];
+ phys_addr_t res[RSND_BASE_MAX];
struct regmap *regmap[RSND_BASE_MAX];
+
+ /* RSND_REG_MAX base */
struct regmap_field *regs[RSND_REG_MAX];
- phys_addr_t res[RSND_REG_MAX];
};
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
@@ -79,11 +81,11 @@ u32 rsnd_read(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return 0;
+ regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+
dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
- regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
-
return val;
}
@@ -103,6 +105,22 @@ void rsnd_write(struct rsnd_priv *priv,
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
}
+void rsnd_force_write(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 data)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return;
+
+ dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
+
+ regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
+}
+
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
enum rsnd_reg reg, u32 mask, u32 data)
{
@@ -166,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
+ /* RSND_BASE_MAX base */
gen->base[reg_id] = base;
gen->regmap[reg_id] = regmap;
gen->res[reg_id] = res->start;
@@ -182,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
if (IS_ERR(regs))
return PTR_ERR(regs);
+ /* RSND_REG_MAX base */
gen->regs[conf[i].idx] = regs;
}
@@ -200,12 +220,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
/* FIXME: it needs SSI_MODE2/3 in the future */
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
- RSND_GEN_M_REG(BUSIF_DALIGN, 0x8, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80),
RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80),
- RSND_GEN_M_REG(INT_ENABLE, 0x18, 0x80),
+ RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
};
struct rsnd_regmap_field_conf conf_scu[] = {
RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20),
+ RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20),
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20),
@@ -214,7 +235,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc),
RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0),
- RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4),
+ RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4),
RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
@@ -223,6 +244,18 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
+ RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
+ RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
+ RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
+ RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
+ RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),
+ RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40),
+ RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40),
+ RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40),
+ RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40),
+ RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40),
+ RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40),
+ RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40),
RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100),
RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100),
RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
diff --git a/kernel/sound/soc/sh/rcar/mix.c b/kernel/sound/soc/sh/rcar/mix.c
new file mode 100644
index 000000000..953dd0be9
--- /dev/null
+++ b/kernel/sound/soc/sh/rcar/mix.c
@@ -0,0 +1,198 @@
+/*
+ * mix.c
+ *
+ * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "rsnd.h"
+
+#define MIX_NAME_SIZE 16
+#define MIX_NAME "mix"
+
+struct rsnd_mix {
+ struct rsnd_mix_platform_info *info; /* rcar_snd.h */
+ struct rsnd_mod mod;
+};
+
+#define rsnd_mix_nr(priv) ((priv)->mix_nr)
+#define for_each_rsnd_mix(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_mix_nr(priv)) && \
+ ((pos) = (struct rsnd_mix *)(priv)->mix + i); \
+ i++)
+
+
+static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, MIX_SWRSR, 0);
+ rsnd_mod_write(mod, MIX_SWRSR, 1);
+}
+
+#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1)
+#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0)
+static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
+{
+ rsnd_mod_write(mod, MIX_MIXIR, enable);
+}
+
+static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+
+ /* Disable MIX dB setting */
+ rsnd_mod_write(mod, MIX_MDBER, 0);
+
+ rsnd_mod_write(mod, MIX_MDBAR, 0);
+ rsnd_mod_write(mod, MIX_MDBBR, 0);
+ rsnd_mod_write(mod, MIX_MDBCR, 0);
+ rsnd_mod_write(mod, MIX_MDBDR, 0);
+
+ /* Enable MIX dB setting */
+ rsnd_mod_write(mod, MIX_MDBER, 1);
+}
+
+static int rsnd_mix_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_power_on(mod);
+
+ rsnd_mix_soft_reset(mod);
+
+ rsnd_mix_initialize_lock(mod);
+
+ rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
+
+ rsnd_path_parse(priv, io);
+
+ /* volume step */
+ rsnd_mod_write(mod, MIX_MIXMR, 0);
+ rsnd_mod_write(mod, MIX_MVPDR, 0);
+
+ rsnd_mix_volume_update(io, mod);
+
+ rsnd_mix_initialize_unlock(mod);
+
+ return 0;
+}
+
+static int rsnd_mix_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_power_off(mod);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_mix_ops = {
+ .name = MIX_NAME,
+ .init = rsnd_mix_init,
+ .quit = rsnd_mix_quit,
+};
+
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
+}
+
+static void rsnd_of_parse_mix(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct rsnd_mix_platform_info *mix_info;
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = &pdev->dev;
+ int nr;
+
+ if (!of_data)
+ return;
+
+ node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
+ if (!node)
+ return;
+
+ nr = of_get_child_count(node);
+ if (!nr)
+ goto rsnd_of_parse_mix_end;
+
+ mix_info = devm_kzalloc(dev,
+ sizeof(struct rsnd_mix_platform_info) * nr,
+ GFP_KERNEL);
+ if (!mix_info) {
+ dev_err(dev, "mix info allocation error\n");
+ goto rsnd_of_parse_mix_end;
+ }
+
+ info->mix_info = mix_info;
+ info->mix_info_nr = nr;
+
+rsnd_of_parse_mix_end:
+ of_node_put(node);
+
+}
+
+int rsnd_mix_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mix *mix;
+ struct clk *clk;
+ char name[MIX_NAME_SIZE];
+ int i, nr, ret;
+
+ /* This driver doesn't support Gen1 at this point */
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ rsnd_of_parse_mix(pdev, of_data, priv);
+
+ nr = info->mix_info_nr;
+ if (!nr)
+ return 0;
+
+ mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
+ if (!mix)
+ return -ENOMEM;
+
+ priv->mix_nr = nr;
+ priv->mix = mix;
+
+ for_each_rsnd_mix(mix, priv, i) {
+ snprintf(name, MIX_NAME_SIZE, "%s.%d",
+ MIX_NAME, i);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ mix->info = &info->mix_info[i];
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
+ clk, RSND_MOD_MIX, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void rsnd_mix_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_mix *mix;
+ int i;
+
+ for_each_rsnd_mix(mix, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(mix));
+ }
+}
diff --git a/kernel/sound/soc/sh/rcar/rcar_snd.h b/kernel/sound/soc/sh/rcar/rcar_snd.h
new file mode 100644
index 000000000..d8e33d38d
--- /dev/null
+++ b/kernel/sound/soc/sh/rcar/rcar_snd.h
@@ -0,0 +1,117 @@
+/*
+ * Renesas R-Car SRU/SCU/SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef RCAR_SND_H
+#define RCAR_SND_H
+
+
+#define RSND_GEN1_SRU 0
+#define RSND_GEN1_ADG 1
+#define RSND_GEN1_SSI 2
+
+#define RSND_GEN2_SCU 0
+#define RSND_GEN2_ADG 1
+#define RSND_GEN2_SSIU 2
+#define RSND_GEN2_SSI 3
+
+#define RSND_BASE_MAX 4
+
+/*
+ * flags
+ *
+ * 0xAB000000
+ *
+ * A : clock sharing settings
+ * B : SSI direction
+ */
+#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
+#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
+
+#define RSND_SSI(_dma_id, _irq, _flags) \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
+#define RSND_SSI_UNUSED \
+{ .dma_id = -1, .irq = -1, .flags = 0 }
+
+struct rsnd_ssi_platform_info {
+ int dma_id;
+ int irq;
+ u32 flags;
+};
+
+#define RSND_SRC(rate, _dma_id) \
+{ .convert_rate = rate, .dma_id = _dma_id, }
+#define RSND_SRC_UNUSED \
+{ .convert_rate = 0, .dma_id = -1, }
+
+struct rsnd_src_platform_info {
+ u32 convert_rate; /* sampling rate convert */
+ int dma_id; /* for Gen2 SCU */
+ int irq;
+};
+
+/*
+ * flags
+ */
+struct rsnd_ctu_platform_info {
+ u32 flags;
+};
+
+struct rsnd_mix_platform_info {
+ u32 flags;
+};
+
+struct rsnd_dvc_platform_info {
+ u32 flags;
+};
+
+struct rsnd_dai_path_info {
+ struct rsnd_ssi_platform_info *ssi;
+ struct rsnd_src_platform_info *src;
+ struct rsnd_ctu_platform_info *ctu;
+ struct rsnd_mix_platform_info *mix;
+ struct rsnd_dvc_platform_info *dvc;
+};
+
+struct rsnd_dai_platform_info {
+ struct rsnd_dai_path_info playback;
+ struct rsnd_dai_path_info capture;
+};
+
+/*
+ * flags
+ *
+ * 0x0000000A
+ *
+ * A : generation
+ */
+#define RSND_GEN_MASK (0xF << 0)
+#define RSND_GEN1 (1 << 0) /* fixme */
+#define RSND_GEN2 (2 << 0) /* fixme */
+
+struct rcar_snd_info {
+ u32 flags;
+ struct rsnd_ssi_platform_info *ssi_info;
+ int ssi_info_nr;
+ struct rsnd_src_platform_info *src_info;
+ int src_info_nr;
+ struct rsnd_ctu_platform_info *ctu_info;
+ int ctu_info_nr;
+ struct rsnd_mix_platform_info *mix_info;
+ int mix_info_nr;
+ struct rsnd_dvc_platform_info *dvc_info;
+ int dvc_info_nr;
+ struct rsnd_dai_platform_info *dai_info;
+ int dai_info_nr;
+ int (*start)(int id);
+ int (*stop)(int id);
+};
+
+#endif
diff --git a/kernel/sound/soc/sh/rcar/rsnd.h b/kernel/sound/soc/sh/rcar/rsnd.h
index 4e6de6804..085329878 100644
--- a/kernel/sound/soc/sh/rcar/rsnd.h
+++ b/kernel/sound/soc/sh/rcar/rsnd.h
@@ -21,10 +21,11 @@
#include <linux/of_irq.h>
#include <linux/sh_dma.h>
#include <linux/workqueue.h>
-#include <sound/rcar_snd.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
+#include "rcar_snd.h"
+
/*
* pseudo register
*
@@ -47,6 +48,18 @@ enum rsnd_reg {
RSND_REG_SCU_SYS_STATUS0,
RSND_REG_SCU_SYS_INT_EN0,
RSND_REG_CMD_ROUTE_SLCT,
+ RSND_REG_CTU_CTUIR,
+ RSND_REG_CTU_ADINR,
+ RSND_REG_MIX_SWRSR,
+ RSND_REG_MIX_MIXIR,
+ RSND_REG_MIX_ADINR,
+ RSND_REG_MIX_MIXMR,
+ RSND_REG_MIX_MVPDR,
+ RSND_REG_MIX_MDBAR,
+ RSND_REG_MIX_MDBBR,
+ RSND_REG_MIX_MDBCR,
+ RSND_REG_MIX_MDBDR,
+ RSND_REG_MIX_MDBER,
RSND_REG_DVC_SWRSR,
RSND_REG_DVC_DVUIR,
RSND_REG_DVC_ADINR,
@@ -99,6 +112,7 @@ enum rsnd_reg {
RSND_REG_SHARE26,
RSND_REG_SHARE27,
RSND_REG_SHARE28,
+ RSND_REG_SHARE29,
RSND_REG_MAX,
};
@@ -119,7 +133,7 @@ enum rsnd_reg {
#define RSND_REG_SSI_CTRL RSND_REG_SHARE02
#define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03
#define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04
-#define RSND_REG_INT_ENABLE RSND_REG_SHARE05
+#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05
#define RSND_REG_SRC_BSDSR RSND_REG_SHARE06
#define RSND_REG_SRC_BSISR RSND_REG_SHARE07
#define RSND_REG_DIV_EN RSND_REG_SHARE08
@@ -136,13 +150,14 @@ enum rsnd_reg {
#define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19
#define RSND_REG_CMD_CTRL RSND_REG_SHARE20
#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21
-#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22
+#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22
#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23
#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24
#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25
#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26
#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27
#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28
+#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29
struct rsnd_of_data;
struct rsnd_priv;
@@ -157,27 +172,28 @@ struct rsnd_dai_stream;
rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
#define rsnd_mod_write(m, r, d) \
rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
+#define rsnd_mod_force_write(m, r, d) \
+ rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
#define rsnd_mod_bset(m, r, s, d) \
rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);
void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
enum rsnd_reg reg, u32 data);
+void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 data);
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 mask, u32 data);
-u32 rsnd_get_adinr(struct rsnd_mod *mod);
+u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+void rsnd_path_parse(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io);
/*
* R-Car DMA
*/
struct rsnd_dma;
-struct rsnd_dma_ops {
- void (*start)(struct rsnd_dma *dma);
- void (*stop)(struct rsnd_dma *dma);
- int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
- struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
- void (*quit)(struct rsnd_dma *dma);
-};
struct rsnd_dmaen {
struct dma_chan *chan;
@@ -199,24 +215,25 @@ struct rsnd_dma {
};
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-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);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-
/*
* R-Car sound mod
*/
enum rsnd_mod_type {
RSND_MOD_DVC = 0,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
RSND_MOD_SRC,
RSND_MOD_SSI,
RSND_MOD_MAX,
@@ -224,25 +241,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 +279,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 +327,26 @@ 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)
+#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
+#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
+#define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
+#define rsnd_mod_get(ip) (&(ip)->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,
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
@@ -320,15 +363,18 @@ struct rsnd_dai_stream {
int byte_per_period;
int next_period_byte;
};
-#define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI])
-#define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC])
-#define rsnd_io_to_mod_dvc(io) ((io)->mod[RSND_MOD_DVC])
+#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
+#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
+#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
+#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
+#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
+#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
#define rsnd_io_to_rdai(io) ((io)->rdai)
#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
#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 +400,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);
/*
@@ -411,12 +458,6 @@ struct rsnd_priv {
void *gen;
/*
- * below value will be filled on rsnd_src_probe()
- */
- void *src;
- int src_nr;
-
- /*
* below value will be filled on rsnd_adg_probe()
*/
void *adg;
@@ -433,6 +474,24 @@ struct rsnd_priv {
int ssi_nr;
/*
+ * below value will be filled on rsnd_src_probe()
+ */
+ void *src;
+ int src_nr;
+
+ /*
+ * below value will be filled on rsnd_ctu_probe()
+ */
+ void *ctu;
+ int ctu_nr;
+
+ /*
+ * below value will be filled on rsnd_mix_probe()
+ */
+ void *mix;
+ int mix_nr;
+
+ /*
* below value will be filled on rsnd_dvc_probe()
*/
void *dvc;
@@ -449,8 +508,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 +517,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,26 +538,48 @@ 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);
/*
+ * R-Car SSI
+ */
+int rsnd_ssi_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv);
+void rsnd_ssi_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+
+#define rsnd_ssi_is_pin_sharing(io) \
+ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+
+/*
* R-Car SRC
*/
int rsnd_src_probe(struct platform_device *pdev,
@@ -512,25 +592,34 @@ 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);
-#define rsnd_src_nr(priv) ((priv)->src_nr)
+/*
+ * R-Car CTU
+ */
+int rsnd_ctu_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv);
+
+void rsnd_ctu_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
/*
- * R-Car SSI
+ * R-Car MIX
*/
-int rsnd_ssi_probe(struct platform_device *pdev,
+int rsnd_mix_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
-void rsnd_ssi_remove(struct platform_device *pdev,
+
+void rsnd_mix_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
-int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
-int rsnd_ssi_use_busif(struct rsnd_mod *mod);
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
/*
* R-Car DVC
@@ -542,7 +631,15 @@ void rsnd_dvc_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
-
+#ifdef DEBUG
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
+#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
+#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
+#else
+#define rsnd_mod_confirm_ssi(mssi)
+#define rsnd_mod_confirm_src(msrc)
+#define rsnd_mod_confirm_dvc(mdvc)
+#endif
#endif
diff --git a/kernel/sound/soc/sh/rcar/rsrc-card.c b/kernel/sound/soc/sh/rcar/rsrc-card.c
index a68517afe..d61db9c38 100644
--- a/kernel/sound/soc/sh/rcar/rsrc-card.c
+++ b/kernel/sound/soc/sh/rcar/rsrc-card.c
@@ -41,65 +41,53 @@ static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = {
static const struct of_device_id rsrc_card_of_match[] = {
{ .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 },
{ .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 },
+ { .compatible = "renesas,rsrc-card", },
{},
};
MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
+#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 +95,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 +132,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 +147,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,29 +198,88 @@ 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;
+
+ of_data = rsrc_dev_to_of_data(dev);
+
+ /* 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";
+
+ /* 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 */
+ if (of_data) {
+ priv->codec_conf.of_node = dai_link->codec_of_node;
+ priv->codec_conf.name_prefix = of_data->prefix;
+ } else {
+ snd_soc_of_parse_audio_prefix(&priv->snd_card,
+ &priv->codec_conf,
+ dai_link->codec_of_node,
+ "audio-prefix");
+ }
- /* Get dai->name */
- ret = snd_soc_of_get_dai_name(np, dai_name);
- if (ret < 0)
- return ret;
+ /* set dai_name */
+ snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s",
+ dai_link->codec_dai_name);
+ }
- /*
- * FIXME
- *
- * rsrc assumes DPCM playback/capture
- */
- dai_link->dpcm_playback = 1;
- dai_link->dpcm_capture = 1;
+ /* 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;
- 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;
- }
+ 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>"
@@ -246,173 +289,98 @@ 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;
+ 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;
+
+ /* 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;
+
+ if (of_data) {
+ priv->snd_card.of_dapm_routes = of_data->routes;
+ priv->snd_card.num_of_dapm_routes = of_data->num_routes;
+ } else {
+ snd_soc_of_parse_audio_routing(&priv->snd_card,
+ "audio-routing");
+ }
+
/* Parse the card name from DT */
snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
- /* DAPM routes */
- priv->snd_card.of_dapm_routes = of_data->routes;
- priv->snd_card.num_of_dapm_routes = of_data->num_routes;
-
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
@@ -420,11 +388,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 +420,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 +429,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/kernel/sound/soc/sh/rcar/src.c b/kernel/sound/soc/sh/rcar/src.c
index 3beb32eb4..68b439ed2 100644
--- a/kernel/sound/soc/sh/rcar/src.c
+++ b/kernel/sound/soc/sh/rcar/src.c
@@ -30,6 +30,7 @@ struct rsnd_src {
#define RSND_SRC_NAME_SIZE 16
+#define rsnd_src_nr(priv) ((priv)->src_nr)
#define rsnd_enable_sync_convert(src) ((src)->sen.val)
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
@@ -117,10 +118,24 @@ struct rsnd_src {
/*
* Gen1/Gen2 common functions
*/
-static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
+static void rsnd_src_soft_reset(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, SRC_SWRSR, 0);
+ rsnd_mod_write(mod, SRC_SWRSR, 1);
+}
+
+
+#define rsnd_src_initialize_lock(mod) __rsnd_src_initialize_lock(mod, 1)
+#define rsnd_src_initialize_unlock(mod) __rsnd_src_initialize_lock(mod, 0)
+static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable)
+{
+ rsnd_mod_write(mod, SRC_SRCIR, enable);
+}
+
+static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
{
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,11 +144,10 @@ 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);
/*
@@ -145,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
/*
* SSI_MODE1
*/
- if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
+ if (rsnd_ssi_is_pin_sharing(io)) {
int shift = -1;
switch (ssi_id) {
case 1:
@@ -170,33 +184,21 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
* DMA settings for SSIU
*/
if (use_busif) {
- u32 val = 0x76543210;
- u32 mask = ~0;
+ u32 val = rsnd_get_dalign(ssi_mod, io);
rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
- rsnd_get_adinr(ssi_mod));
+ rsnd_get_adinr_bit(ssi_mod, io));
rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
- mask <<= runtime->channels * 4;
- val = val & mask;
-
- switch (runtime->sample_bits) {
- case 16:
- val |= 0x67452301 & ~mask;
- break;
- case 32:
- val |= 0x76543210 & ~mask;
- break;
- }
- rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val);
-
+ rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val);
}
return 0;
}
-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
@@ -214,10 +216,9 @@ int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod)
return 0;
/* enable SSI interrupt if Gen2 */
- if (rsnd_ssi_is_dma_mode(ssi_mod))
- rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
- else
- rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
+ rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
+ rsnd_ssi_is_dma_mode(ssi_mod) ?
+ 0x0e000000 : 0x0f000000);
return 0;
}
@@ -230,15 +231,14 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
return 0;
/* disable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000);
+ rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
return 0;
}
-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,23 +283,19 @@ 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)
fsrate = 0x0400000 / convert_rate * runtime->rate;
- /* set/clear soft reset */
- rsnd_mod_write(mod, SRC_SWRSR, 0);
- rsnd_mod_write(mod, SRC_SWRSR, 1);
-
/* Set channel number and output bit length */
- rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
+ rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io));
/* Enable the initial value of IFS */
if (fsrate) {
@@ -316,6 +312,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)
{
@@ -355,29 +352,28 @@ static int rsnd_src_init(struct rsnd_mod *mod,
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
+
+ rsnd_src_soft_reset(mod);
+
+ rsnd_src_initialize_lock(mod);
src->err = 0;
/* reset sync convert_rate */
src->sync.val = 0;
- /*
- * Initialize the operation of the SRC internal circuits
- * see rsnd_src_start()
- */
- rsnd_mod_write(mod, SRC_SRCIR, 1);
-
return 0;
}
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);
struct device *dev = rsnd_priv_to_dev(priv);
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
if (src->err)
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
@@ -393,11 +389,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
static int rsnd_src_start(struct rsnd_mod *mod)
{
- /*
- * Cancel the initialization and operate the SRC function
- * see rsnd_src_init()
- */
- rsnd_mod_write(mod, SRC_SRCIR, 0);
+ rsnd_src_initialize_unlock(mod);
return 0;
}
@@ -411,9 +403,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 +440,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 +498,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 +516,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 +525,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 +534,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 +550,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 +561,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);
@@ -611,6 +607,14 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
int_val = 0;
}
+ /*
+ * WORKAROUND
+ *
+ * ignore over flow error when rsnd_enable_sync_convert()
+ */
+ if (rsnd_enable_sync_convert(src))
+ sys_int_val = sys_int_val & 0xffff;
+
rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
@@ -626,11 +630,22 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod)
static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod)
{
- u32 val = OUF_SRC(rsnd_mod_id(mod));
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ u32 val0, val1;
bool ret = false;
- if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) ||
- (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) {
+ val0 = val1 = OUF_SRC(rsnd_mod_id(mod));
+
+ /*
+ * WORKAROUND
+ *
+ * ignore over flow error when rsnd_enable_sync_convert()
+ */
+ if (rsnd_enable_sync_convert(src))
+ val0 = val0 & 0xffff;
+
+ if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) ||
+ (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) {
struct rsnd_src *src = rsnd_mod_to_src(mod);
src->err++;
@@ -643,10 +658,23 @@ 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;
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ u32 val;
+
+ val = rsnd_get_dalign(mod, io);
+
+ rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val);
+
+ /*
+ * WORKAROUND
+ *
+ * Enable SRC output if you want to use sync convert together with DVC
+ */
+ val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ?
+ 0x01 : 0x11;
rsnd_mod_write(mod, SRC_CTRL, val);
@@ -670,13 +698,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 +719,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 +762,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 +798,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 +817,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 +839,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 +847,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 +865,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 +877,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)
@@ -872,12 +918,12 @@ static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
rsnd_mod_write(mod, SRC_IFSVR, fsrate);
}
-static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+static int rsnd_src_pcm_new_gen2(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_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -886,28 +932,21 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
*/
/*
- * Gen1 is not supported
- */
- if (rsnd_is_gen1(priv))
- return 0;
-
- /*
* SRC sync convert needs clock master
*/
if (!rsnd_rdai_is_clk_master(rdai))
return 0;
/*
- * We can't use SRC sync convert
- * if it has DVC
+ * SRC In doesn't work if DVC was enabled
*/
- if (rsnd_io_to_mod_dvc(io))
+ if (dvc && !rsnd_io_is_play(io))
return 0;
/*
* 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 +955,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",
@@ -936,7 +975,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
.start = rsnd_src_start_gen2,
.stop = rsnd_src_stop_gen2,
.hw_params = rsnd_src_hw_params,
- .pcm_new = rsnd_src_pcm_new,
+ .pcm_new = rsnd_src_pcm_new_gen2,
};
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -944,7 +983,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
id = 0;
- return &((struct rsnd_src *)(priv->src) + id)->mod;
+ return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
}
static void rsnd_of_parse_src(struct platform_device *pdev,
@@ -1004,8 +1043,10 @@ int rsnd_src_probe(struct platform_device *pdev,
int i, nr, ret;
ops = NULL;
- if (rsnd_is_gen1(priv))
+ if (rsnd_is_gen1(priv)) {
ops = &rsnd_src_gen1_ops;
+ dev_warn(dev, "Gen1 support will be removed soon\n");
+ }
if (rsnd_is_gen2(priv))
ops = &rsnd_src_gen2_ops;
if (!ops) {
@@ -1023,10 +1064,8 @@ int rsnd_src_probe(struct platform_device *pdev,
return 0;
src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL);
- if (!src) {
- dev_err(dev, "SRC allocate failed\n");
+ if (!src)
return -ENOMEM;
- }
priv->src_nr = nr;
priv->src = src;
@@ -1041,7 +1080,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, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
if (ret)
return ret;
}
@@ -1056,6 +1095,6 @@ void rsnd_src_remove(struct platform_device *pdev,
int i;
for_each_rsnd_src(src, priv, i) {
- rsnd_mod_quit(&src->mod);
+ rsnd_mod_quit(rsnd_mod_get(src));
}
}
diff --git a/kernel/sound/soc/sh/rcar/ssi.c b/kernel/sound/soc/sh/rcar/ssi.c
index 7bb9c087f..1427ec21b 100644
--- a/kernel/sound/soc/sh/rcar/ssi.c
+++ b/kernel/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;
};
@@ -78,18 +79,17 @@ struct rsnd_ssi {
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
#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 = rsnd_io_to_mod_ssi(io);
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))
@@ -128,10 +128,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
- int i, j, ret;
- int adg_clk_div_table[] = {
- 1, 6, /* see adg.c */
- };
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ int j, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
@@ -141,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* Find best clock, and try to start ADG
*/
- for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) {
- for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
-
- /*
- * this driver is assuming that
- * system word is 64fs (= 2 x 32bit)
- * see rsnd_ssi_init()
- */
- main_rate = rate / adg_clk_div_table[i]
- * 32 * 2 * ssi_clk_mul_table[j];
-
- ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
- if (0 == ret) {
- ssi->cr_clk = FORCE | SWL_32 |
- SCKD | SWSD | CKDV(j);
-
- dev_dbg(dev, "%s[%d] outputs %u Hz\n",
- rsnd_mod_name(&ssi->mod),
- rsnd_mod_id(&ssi->mod), rate);
-
- return 0;
- }
+ for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+ /*
+ * this driver is assuming that
+ * system word is 64fs (= 2 x 32bit)
+ * see rsnd_ssi_init()
+ */
+ main_rate = rate * 32 * 2 * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+ if (0 == ret) {
+ ssi->cr_clk = FORCE | SWL_32 |
+ SCKD | SWSD | CKDV(j);
+
+ dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+ rsnd_mod_name(mod),
+ rsnd_mod_id(mod), rate);
+
+ return 0;
}
}
@@ -172,8 +167,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
{
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
ssi->cr_clk = 0;
- rsnd_adg_ssi_clk_stop(&ssi->mod);
+ rsnd_adg_ssi_clk_stop(mod);
}
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
@@ -182,55 +179,62 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
u32 cr_mode;
u32 cr;
if (0 == ssi->usrcnt) {
- rsnd_mod_hw_start(&ssi->mod);
+ rsnd_mod_power_on(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(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);
+ rsnd_mod_write(mod, SSICR, cr);
/* enable WS continue */
if (rsnd_rdai_is_clk_master(rdai))
- rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+ rsnd_mod_write(mod, SSIWSR, CONT);
/* clear error status */
- rsnd_mod_write(&ssi->mod, SSISR, 0);
+ rsnd_mod_write(mod, SSISR, 0);
ssi->usrcnt++;
dev_dbg(dev, "%s[%d] hw started\n",
- rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+ rsnd_mod_name(mod), rsnd_mod_id(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_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(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--;
@@ -242,38 +246,42 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
cr = ssi->cr_own |
ssi->cr_clk;
- rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
- rsnd_ssi_status_check(&ssi->mod, DIRQ);
+ rsnd_mod_write(mod, SSICR, cr | EN);
+ rsnd_ssi_status_check(mod, DIRQ);
/*
* disable SSI,
* and, wait idle state
*/
- rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */
- rsnd_ssi_status_check(&ssi->mod, IIRQ);
+ rsnd_mod_write(mod, SSICR, cr); /* disabled all */
+ rsnd_ssi_status_check(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);
+ rsnd_mod_power_off(mod);
+
+ ssi->chan = 0;
}
dev_dbg(dev, "%s[%d] hw stopped\n",
- rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
/*
* 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 +329,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,24 +345,57 @@ 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(rsnd_mod_get(ssi_parent), io,
+ substream, params);
+
+ return 0;
+}
+
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
{
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
/* under/over flow error */
if (status & (UIRQ | OIRQ)) {
ssi->err++;
/* clear error status */
- rsnd_mod_write(&ssi->mod, SSISR, 0);
+ rsnd_mod_write(mod, SSISR, 0);
}
}
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));
rsnd_ssi_hw_start(ssi, io);
@@ -363,6 +405,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 +414,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 +454,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 +467,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 +496,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 +506,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 +518,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,33 +533,35 @@ 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);
+ devm_free_irq(dev, irq, mod);
return 0;
}
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 +582,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))
name = is_play ? "rxu" : "txu";
else
name = is_play ? "rx" : "tx";
@@ -565,6 +633,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)
@@ -588,22 +657,24 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
id = 0;
- return &((struct rsnd_ssi *)(priv->ssi) + id)->mod;
+ return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
}
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(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))
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
+ if (!__rsnd_ssi_is_pin_sharing(mod))
return;
- switch (rsnd_mod_id(&ssi->mod)) {
+ switch (rsnd_mod_id(mod)) {
case 1:
case 2:
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
@@ -629,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
struct device *dev = &pdev->dev;
int nr, i;
- if (!of_data)
- return;
-
node = rsnd_ssi_of_node(priv);
if (!node)
return;
@@ -702,10 +770,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
*/
nr = info->ssi_info_nr;
ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
- if (!ssi) {
- dev_err(dev, "SSI allocate failed\n");
+ if (!ssi)
return -ENOMEM;
- }
priv->ssi = ssi;
priv->ssi_nr = nr;
@@ -728,11 +794,12 @@ 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, rsnd_mod_get(ssi), 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;
@@ -745,6 +812,6 @@ void rsnd_ssi_remove(struct platform_device *pdev,
int i;
for_each_rsnd_ssi(ssi, priv, i) {
- rsnd_mod_quit(&ssi->mod);
+ rsnd_mod_quit(rsnd_mod_get(ssi));
}
}
diff --git a/kernel/sound/soc/sh/siu_dai.c b/kernel/sound/soc/sh/siu_dai.c
index abb0d9562..76b2ab8c2 100644
--- a/kernel/sound/soc/sh/siu_dai.c
+++ b/kernel/sound/soc/sh/siu_dai.c
@@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev)
struct siu_info *info;
int ret;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
siu_i2s_data = info;
@@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev)
ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
if (ret)
- goto ereqfw;
+ return ret;
/*
* Loaded firmware is "const" - read only, but we have to modify it in
@@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev)
release_firmware(fw_entry);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto egetres;
- }
+ if (!res)
+ return -ENODEV;
- region = request_mem_region(res->start, resource_size(res),
- pdev->name);
+ region = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name);
if (!region) {
dev_err(&pdev->dev, "SIU region already claimed\n");
- ret = -EBUSY;
- goto ereqmemreg;
+ return -EBUSY;
}
- ret = -ENOMEM;
- info->pram = ioremap(res->start, PRAM_SIZE);
+ info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
if (!info->pram)
- goto emappram;
- info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+ return -ENOMEM;
+ info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
+ XRAM_SIZE);
if (!info->xram)
- goto emapxram;
- info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+ return -ENOMEM;
+ info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
+ YRAM_SIZE);
if (!info->yram)
- goto emapyram;
- info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
- REG_OFFSET);
+ return -ENOMEM;
+ info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
+ resource_size(res) - REG_OFFSET);
if (!info->reg)
- goto emapreg;
+ return -ENOMEM;
dev_set_drvdata(&pdev->dev, info);
/* register using ARRAY version so we can keep dai name */
- ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component,
- &siu_i2s_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+ &siu_i2s_dai, 1);
if (ret < 0)
- goto edaiinit;
+ return ret;
- ret = snd_soc_register_platform(&pdev->dev, &siu_platform);
+ ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform);
if (ret < 0)
- goto esocregp;
+ return ret;
pm_runtime_enable(&pdev->dev);
- return ret;
-
-esocregp:
- snd_soc_unregister_component(&pdev->dev);
-edaiinit:
- iounmap(info->reg);
-emapreg:
- iounmap(info->yram);
-emapyram:
- iounmap(info->xram);
-emapxram:
- iounmap(info->pram);
-emappram:
- release_mem_region(res->start, resource_size(res));
-ereqmemreg:
-egetres:
-ereqfw:
- kfree(info);
-
- return ret;
+ return 0;
}
static int siu_remove(struct platform_device *pdev)
{
- struct siu_info *info = dev_get_drvdata(&pdev->dev);
- struct resource *res;
-
pm_runtime_disable(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
-
- iounmap(info->reg);
- iounmap(info->yram);
- iounmap(info->xram);
- iounmap(info->pram);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res)
- release_mem_region(res->start, resource_size(res));
- kfree(info);
-
return 0;
}
diff --git a/kernel/sound/soc/sh/ssi.c b/kernel/sound/soc/sh/ssi.c
index ab13146e4..89ed1b107 100644
--- a/kernel/sound/soc/sh/ssi.c
+++ b/kernel/sound/soc/sh/ssi.c
@@ -385,14 +385,9 @@ static const struct snd_soc_component_driver sh4_ssi_component = {
static int sh4_soc_dai_probe(struct platform_device *pdev)
{
- return snd_soc_register_component(&pdev->dev, &sh4_ssi_component,
- sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
-}
-
-static int sh4_soc_dai_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_component(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component,
+ sh4_ssi_dai,
+ ARRAY_SIZE(sh4_ssi_dai));
}
static struct platform_driver sh4_ssi_driver = {
@@ -401,7 +396,6 @@ static struct platform_driver sh4_ssi_driver = {
},
.probe = sh4_soc_dai_probe,
- .remove = sh4_soc_dai_remove,
};
module_platform_driver(sh4_ssi_driver);
diff --git a/kernel/sound/soc/soc-ac97.c b/kernel/sound/soc/soc-ac97.c
index 08d7259bb..d40efc9fe 100644
--- a/kernel/sound/soc/soc-ac97.c
+++ b/kernel/sound/soc/soc-ac97.c
@@ -85,10 +85,19 @@ EXPORT_SYMBOL(snd_soc_alloc_ac97_codec);
/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
+ * @id: The expected device ID
+ * @id_mask: Mask that is applied to the device ID before comparing with @id
*
* Initialises AC97 codec resources for use by ad-hoc devices only.
+ *
+ * If @id is not 0 this function will reset the device, then read the ID from
+ * the device and check if it matches the expected ID. If it doesn't match an
+ * error will be returned and device will not be registered.
+ *
+ * Returns: A PTR_ERR() on failure or a valid snd_ac97 struct on success.
*/
-struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
+ unsigned int id, unsigned int id_mask)
{
struct snd_ac97 *ac97;
int ret;
@@ -97,13 +106,24 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
if (IS_ERR(ac97))
return ac97;
- ret = device_add(&ac97->dev);
- if (ret) {
- put_device(&ac97->dev);
- return ERR_PTR(ret);
+ if (id) {
+ ret = snd_ac97_reset(ac97, false, id, id_mask);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset AC97 device: %d\n",
+ ret);
+ goto err_put_device;
+ }
}
+ ret = device_add(&ac97->dev);
+ if (ret)
+ goto err_put_device;
+
return ac97;
+
+err_put_device:
+ put_device(&ac97->dev);
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
diff --git a/kernel/sound/soc/soc-compress.c b/kernel/sound/soc/soc-compress.c
index 025c38fbe..bb82bb966 100644
--- a/kernel/sound/soc/soc-compress.c
+++ b/kernel/sound/soc/soc-compress.c
@@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = {
.get_codec_caps = soc_compr_get_codec_caps
};
-/* create a new compress */
-int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
+/**
+ * snd_soc_new_compress - create a new compress.
+ *
+ * @rtd: The runtime for which we will create compress
+ * @num: the device index number (zero based - shared with normal PCMs)
+ *
+ * Return: 0 for success, else error.
+ */
+int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
@@ -623,6 +630,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
struct snd_pcm *be_pcm;
char new_name[64];
int ret = 0, direction = 0;
+ int playback = 0, capture = 0;
if (rtd->num_codecs > 1) {
dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n");
@@ -634,11 +642,27 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
rtd->dai_link->stream_name, codec_dai->name, num);
if (codec_dai->driver->playback.channels_min)
+ playback = 1;
+ if (codec_dai->driver->capture.channels_min)
+ capture = 1;
+
+ capture = capture && cpu_dai->driver->capture.channels_min;
+ playback = playback && cpu_dai->driver->playback.channels_min;
+
+ /*
+ * Compress devices are unidirectional so only one of the directions
+ * should be set, check for that (xor)
+ */
+ if (playback + capture != 1) {
+ dev_err(rtd->card->dev, "Invalid direction for compress P %d, C %d\n",
+ playback, capture);
+ return -EINVAL;
+ }
+
+ if(playback)
direction = SND_COMPRESS_PLAYBACK;
- else if (codec_dai->driver->capture.channels_min)
- direction = SND_COMPRESS_CAPTURE;
else
- return -EINVAL;
+ direction = SND_COMPRESS_CAPTURE;
compr = kzalloc(sizeof(*compr), GFP_KERNEL);
if (compr == NULL) {
@@ -703,3 +727,4 @@ compr_err:
kfree(compr);
return ret;
}
+EXPORT_SYMBOL_GPL(snd_soc_new_compress);
diff --git a/kernel/sound/soc/soc-core.c b/kernel/sound/soc/soc-core.c
index 23732523f..a1305f827 100644
--- a/kernel/sound/soc/soc-core.c
+++ b/kernel/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;
}
@@ -662,10 +654,12 @@ int snd_soc_suspend(struct device *dev)
/* suspend all CODECs */
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
/* If there are paths active then the CODEC will be held with
* bias _ON and should not be suspended. */
if (!codec->suspended) {
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_STANDBY:
/*
* If the CODEC is capable of idle
@@ -673,7 +667,7 @@ int snd_soc_suspend(struct device *dev)
* means it's doing something,
* otherwise fall through.
*/
- if (codec->dapm.idle_bias_off) {
+ if (dapm->idle_bias_off) {
dev_dbg(codec->dev,
"ASoC: idle_bias_off CODEC on over suspend\n");
break;
@@ -750,23 +744,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;
}
}
@@ -814,12 +795,12 @@ static void soc_resume_deferred(struct work_struct *work)
dev_dbg(card->dev, "ASoC: resume work completed\n");
- /* userspace can access us now we are back as we were before */
- snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
-
/* Recheck all endpoints too, their state is affected by suspend */
dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
+
+ /* userspace can access us now we are back as we were before */
+ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
}
/* powers up audio subsystem after a suspend */
@@ -904,12 +885,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;
@@ -994,7 +980,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
static void soc_remove_component(struct snd_soc_component *component)
{
- if (!component->probed)
+ if (!component->card)
return;
/* This is a HACK and will be removed soon */
@@ -1007,7 +993,7 @@ static void soc_remove_component(struct snd_soc_component *component)
snd_soc_dapm_free(snd_soc_component_get_dapm(component));
soc_cleanup_component_debugfs(component);
- component->probed = 0;
+ component->card = NULL;
module_put(component->dev->driver->owner);
}
@@ -1118,16 +1104,26 @@ static int soc_probe_component(struct snd_soc_card *card,
struct snd_soc_dai *dai;
int ret;
- if (component->probed)
+ if (!strcmp(component->name, "snd-soc-dummy"))
return 0;
- component->card = card;
- dapm->card = card;
- soc_set_name_prefix(card, component);
+ if (component->card) {
+ if (component->card != card) {
+ dev_err(component->dev,
+ "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n",
+ card->name, component->card->name);
+ return -ENODEV;
+ }
+ return 0;
+ }
if (!try_module_get(component->dev->driver->owner))
return -ENODEV;
+ component->card = card;
+ dapm->card = card;
+ soc_set_name_prefix(card, component);
+
soc_init_component_debugfs(component);
if (component->dapm_widgets) {
@@ -1171,7 +1167,6 @@ static int soc_probe_component(struct snd_soc_card *card,
snd_soc_dapm_add_routes(dapm, component->dapm_routes,
component->num_dapm_routes);
- component->probed = 1;
list_add(&dapm->list, &card->dapm_list);
/* This is a HACK and will be removed soon */
@@ -1182,6 +1177,7 @@ static int soc_probe_component(struct snd_soc_card *card,
err_probe:
soc_cleanup_component_debugfs(component);
+ component->card = NULL;
module_put(component->dev->driver->owner);
return ret;
@@ -1374,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
soc_dpcm_debugfs_add(rtd);
#endif
- if (cpu_dai->driver->compress_dai) {
+ if (cpu_dai->driver->compress_new) {
/*create compress_device"*/
- ret = soc_new_compress(rtd, num);
+ ret = cpu_dai->driver->compress_new(rtd, num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create compress %s\n",
dai_link->stream_name);
@@ -1465,7 +1461,7 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
rtd->dev_registered = 0;
}
- if (component && component->probed)
+ if (component)
soc_remove_component(component);
}
@@ -1732,6 +1728,7 @@ card_probe_error:
if (card->remove)
card->remove(card);
+ snd_soc_dapm_free(&card->dapm);
soc_cleanup_card_debugfs(card);
snd_card_free(card->snd_card);
@@ -2143,7 +2140,7 @@ EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll);
/**
* snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio.
* @dai: DAI
- * @ratio Ratio of BCLK to Sample rate.
+ * @ratio: Ratio of BCLK to Sample rate.
*
* Configures the DAI for a preset BCLK to sample rate ratio.
*/
@@ -2435,6 +2432,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 +2597,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]);
@@ -2665,10 +2664,7 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
component->probe = component->driver->probe;
component->remove = component->driver->remove;
- if (!component->dapm_ptr)
- component->dapm_ptr = &component->dapm;
-
- dapm = component->dapm_ptr;
+ dapm = &component->dapm;
dapm->dev = dev;
dapm->component = component;
dapm->bias_level = SND_SOC_BIAS_OFF;
@@ -2749,6 +2745,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)
@@ -2811,6 +2808,7 @@ EXPORT_SYMBOL_GPL(snd_soc_register_component);
/**
* snd_soc_unregister_component - Unregister a component from the ASoC core
*
+ * @dev: The device to unregister
*/
void snd_soc_unregister_component(struct device *dev)
{
@@ -2825,6 +2823,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);
@@ -2850,7 +2849,7 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component)
* snd_soc_add_platform - Add a platform to the ASoC core
* @dev: The parent device for the platform
* @platform: The platform to add
- * @platform_driver: The driver for the platform
+ * @platform_drv: The driver for the platform
*/
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
const struct snd_soc_platform_driver *platform_drv)
@@ -2889,7 +2888,8 @@ EXPORT_SYMBOL_GPL(snd_soc_add_platform);
/**
* snd_soc_register_platform - Register a platform with the ASoC core
*
- * @platform: platform to register
+ * @dev: The device for the platform
+ * @platform_drv: The driver for the platform
*/
int snd_soc_register_platform(struct device *dev,
const struct snd_soc_platform_driver *platform_drv)
@@ -2950,7 +2950,7 @@ EXPORT_SYMBOL_GPL(snd_soc_lookup_platform);
/**
* snd_soc_unregister_platform - Unregister a platform from the ASoC core
*
- * @platform: platform to unregister
+ * @dev: platform to unregister
*/
void snd_soc_unregister_platform(struct device *dev)
{
@@ -3041,13 +3041,17 @@ static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm,
/**
* snd_soc_register_codec - Register a codec with the ASoC core
*
- * @codec: codec to register
+ * @dev: The parent device for this codec
+ * @codec_drv: Codec driver
+ * @dai_drv: The associated DAI driver
+ * @num_dai: Number of DAIs
*/
int snd_soc_register_codec(struct device *dev,
const struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
+ struct snd_soc_dapm_context *dapm;
struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
int ret, i;
@@ -3058,7 +3062,6 @@ int snd_soc_register_codec(struct device *dev,
if (codec == NULL)
return -ENOMEM;
- codec->component.dapm_ptr = &codec->dapm;
codec->component.codec = codec;
ret = snd_soc_component_initialize(&codec->component,
@@ -3088,12 +3091,14 @@ int snd_soc_register_codec(struct device *dev,
if (codec_drv->read)
codec->component.read = snd_soc_codec_drv_read;
codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
- codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
- codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
+
+ dapm = snd_soc_codec_get_dapm(codec);
+ dapm->idle_bias_off = codec_drv->idle_bias_off;
+ dapm->suspend_bias_off = codec_drv->suspend_bias_off;
if (codec_drv->seq_notifier)
- codec->dapm.seq_notifier = codec_drv->seq_notifier;
+ dapm->seq_notifier = codec_drv->seq_notifier;
if (codec_drv->set_bias_level)
- codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
+ dapm->set_bias_level = snd_soc_codec_set_bias_level;
codec->dev = dev;
codec->driver = codec_drv;
codec->component.val_bytes = codec_drv->reg_word_size;
@@ -3140,7 +3145,7 @@ EXPORT_SYMBOL_GPL(snd_soc_register_codec);
/**
* snd_soc_unregister_codec - Unregister a codec from the ASoC core
*
- * @codec: codec to unregister
+ * @dev: codec to unregister
*/
void snd_soc_unregister_codec(struct device *dev)
{
@@ -3286,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
+static int snd_soc_of_get_slot_mask(struct device_node *np,
+ const char *prop_name,
+ unsigned int *mask)
+{
+ u32 val;
+ const __be32 *of_slot_mask = of_get_property(np, prop_name, &val);
+ int i;
+
+ if (!of_slot_mask)
+ return 0;
+ val /= sizeof(u32);
+ for (i = 0; i < val; i++)
+ if (be32_to_cpup(&of_slot_mask[i]))
+ *mask |= (1 << i);
+
+ return val;
+}
+
int snd_soc_of_parse_tdm_slot(struct device_node *np,
+ unsigned int *tx_mask,
+ unsigned int *rx_mask,
unsigned int *slots,
unsigned int *slot_width)
{
u32 val;
int ret;
+ if (tx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask);
+ if (rx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
+
if (of_property_read_bool(np, "dai-tdm-slot-num")) {
ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
if (ret)
@@ -3315,6 +3345,26 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
+void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
+ struct snd_soc_codec_conf *codec_conf,
+ struct device_node *of_node,
+ const char *propname)
+{
+ struct device_node *np = card->dev->of_node;
+ const char *str;
+ int ret;
+
+ ret = of_property_read_string(np, propname, &str);
+ if (ret < 0) {
+ /* no prefix is not error */
+ return;
+ }
+
+ codec_conf->of_node = of_node;
+ codec_conf->name_prefix = str;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix);
+
int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
const char *propname)
{
@@ -3488,11 +3538,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/kernel/sound/soc/soc-dapm.c b/kernel/sound/soc/soc-dapm.c
index b6c12dccb..416514fe9 100644
--- a/kernel/sound/soc/soc-dapm.c
+++ b/kernel/sound/soc/soc-dapm.c
@@ -47,15 +47,27 @@
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
+#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \
+ SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN)
+
+#define snd_soc_dapm_for_each_direction(dir) \
+ for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
+ (dir)++)
+
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
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 +82,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 +113,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,
@@ -160,45 +174,59 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
}
/*
- * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
- * paths
- * @w: The widget for which to invalidate the cached number of input paths
- *
- * The function resets the cached number of inputs for the specified widget and
- * all widgets that can be reached via outgoing paths from the widget.
- *
- * This function must be called if the number of input paths for a widget might
- * have changed. E.g. if the source state of a widget changes or a path is added
- * or activated with the widget as the sink.
+ * Common implementation for dapm_widget_invalidate_input_paths() and
+ * dapm_widget_invalidate_output_paths(). The function is inlined since the
+ * combined size of the two specialized functions is only marginally larger then
+ * the size of the generic function and at the same time the fast path of the
+ * specialized functions is significantly smaller than the generic function.
*/
-static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+static __always_inline void dapm_widget_invalidate_paths(
+ struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir)
{
- struct snd_soc_dapm_widget *sink;
+ enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ struct snd_soc_dapm_widget *node;
struct snd_soc_dapm_path *p;
LIST_HEAD(list);
dapm_assert_locked(w->dapm);
- if (w->inputs == -1)
+ if (w->endpoints[dir] == -1)
return;
- w->inputs = -1;
list_add_tail(&w->work_list, &list);
+ w->endpoints[dir] = -1;
list_for_each_entry(w, &list, work_list) {
- list_for_each_entry(p, &w->sinks, list_source) {
+ snd_soc_dapm_widget_for_each_path(w, dir, p) {
if (p->is_supply || p->weak || !p->connect)
continue;
- sink = p->sink;
- if (sink->inputs != -1) {
- sink->inputs = -1;
- list_add_tail(&sink->work_list, &list);
+ node = p->node[rdir];
+ if (node->endpoints[dir] != -1) {
+ node->endpoints[dir] = -1;
+ list_add_tail(&node->work_list, &list);
}
}
}
}
/*
+ * dapm_widget_invalidate_input_paths() - Invalidate the cached number of
+ * input paths
+ * @w: The widget for which to invalidate the cached number of input paths
+ *
+ * Resets the cached number of inputs for the specified widget and all widgets
+ * that can be reached via outcoming paths from the widget.
+ *
+ * This function must be called if the number of output paths for a widget might
+ * have changed. E.g. if the source state of a widget changes or a path is added
+ * or activated with the widget as the sink.
+ */
+static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+{
+ dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN);
+}
+
+/*
* dapm_widget_invalidate_output_paths() - Invalidate the cached number of
* output paths
* @w: The widget for which to invalidate the cached number of output paths
@@ -212,29 +240,7 @@ static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
*/
static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
{
- struct snd_soc_dapm_widget *source;
- struct snd_soc_dapm_path *p;
- LIST_HEAD(list);
-
- dapm_assert_locked(w->dapm);
-
- if (w->outputs == -1)
- return;
-
- w->outputs = -1;
- list_add_tail(&w->work_list, &list);
-
- list_for_each_entry(w, &list, work_list) {
- list_for_each_entry(p, &w->sources, list_sink) {
- if (p->is_supply || p->weak || !p->connect)
- continue;
- source = p->source;
- if (source->outputs != -1) {
- source->outputs = -1;
- list_add_tail(&source->work_list, &list);
- }
- }
- }
+ dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT);
}
/*
@@ -263,9 +269,9 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
* endpoints is either connected or disconnected that sum won't change,
* so there is no need to re-check the path.
*/
- if (p->source->inputs != 0)
+ if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0)
dapm_widget_invalidate_input_paths(p->sink);
- if (p->sink->outputs != 0)
+ if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0)
dapm_widget_invalidate_output_paths(p->source);
}
@@ -276,11 +282,11 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
mutex_lock(&card->dapm_mutex);
list_for_each_entry(w, &card->widgets, list) {
- if (w->is_sink || w->is_source) {
+ if (w->is_ep) {
dapm_mark_dirty(w, "Rechecking endpoints");
- if (w->is_sink)
+ if (w->is_ep & SND_SOC_DAPM_EP_SINK)
dapm_widget_invalidate_output_paths(w);
- if (w->is_source)
+ if (w->is_ep & SND_SOC_DAPM_EP_SOURCE)
dapm_widget_invalidate_input_paths(w);
}
}
@@ -308,14 +314,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 +333,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 +350,55 @@ 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);
+ kfree(name);
if (!data->widget) {
- kfree(data);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_data;
+ }
+ }
+ 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_unlocked(
+ widget->dapm, &template);
+ kfree(name);
+ if (!data->widget) {
+ ret = -ENOMEM;
+ goto err_data;
}
+
+ snd_soc_dapm_add_path(widget->dapm, data->widget,
+ widget, NULL, NULL);
}
break;
default:
@@ -357,6 +408,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
kcontrol->private_data = data;
return 0;
+
+err_data:
+ kfree(data);
+ return ret;
}
static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
@@ -405,11 +460,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)
@@ -459,6 +509,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
}
/**
+ * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a
+ * kcontrol
+ * @kcontrol: The kcontrol
+ */
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
+ struct snd_kcontrol *kcontrol)
+{
+ return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);
+
+/**
* snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
* kcontrol
* @kcontrol: The kcontrol
@@ -525,6 +587,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 +670,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 +686,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;
@@ -669,7 +791,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
* Determine if a kcontrol is shared. If it is, look it up. If it isn't,
* create it. Either way, add the widget into the control's widget list
*/
-static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
+static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
int kci)
{
struct snd_soc_dapm_context *dapm = w->dapm;
@@ -700,6 +822,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_pga:
wname_in_long_name = true;
kcname_in_long_name = true;
break;
@@ -707,6 +830,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,25 +901,30 @@ 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++) {
/* match name */
- list_for_each_entry(path, &w->sources, list_sink) {
+ snd_soc_dapm_widget_for_each_source_path(w, path) {
/* mixer/mux paths name must match control name */
if (path->name != (char *)w->kcontrol_news[i].name)
continue;
- if (w->kcontrols[i]) {
- dapm_kcontrol_add_path(w->kcontrols[i], path);
- continue;
+ if (!w->kcontrols[i]) {
+ ret = dapm_create_or_share_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);
}
}
@@ -806,26 +935,41 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
static int dapm_new_mux(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_context *dapm = w->dapm;
+ enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *path;
+ const char *type;
int ret;
+ switch (w->id) {
+ case snd_soc_dapm_mux:
+ dir = SND_SOC_DAPM_DIR_OUT;
+ type = "mux";
+ break;
+ case snd_soc_dapm_demux:
+ dir = SND_SOC_DAPM_DIR_IN;
+ 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(&w->edges[dir])) {
+ dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);
return -EINVAL;
}
- ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
+ ret = dapm_create_or_share_kcontrol(w, 0);
if (ret < 0)
return ret;
- list_for_each_entry(path, &w->sources, list_sink) {
+ snd_soc_dapm_widget_for_each_path(w, dir, path) {
if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
@@ -836,9 +980,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
/* create new dapm volume control */
static int dapm_new_pga(struct snd_soc_dapm_widget *w)
{
- if (w->num_kcontrols)
- dev_err(w->dapm->dev,
- "ASoC: PGA controls not supported: '%s'\n", w->name);
+ int i, ret;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ ret = dapm_create_or_share_kcontrol(w, i);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -893,66 +1041,59 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
}
}
-/* add widget to list if it's not already in the list */
-static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list,
- struct snd_soc_dapm_widget *w)
+static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
+ struct list_head *widgets)
{
- struct snd_soc_dapm_widget_list *wlist;
- int wlistsize, wlistentries, i;
-
- if (*list == NULL)
- return -EINVAL;
-
- wlist = *list;
+ struct snd_soc_dapm_widget *w;
+ struct list_head *it;
+ unsigned int size = 0;
+ unsigned int i = 0;
- /* is this widget already in the list */
- for (i = 0; i < wlist->num_widgets; i++) {
- if (wlist->widgets[i] == w)
- return 0;
- }
+ list_for_each(it, widgets)
+ size++;
- /* allocate some new space */
- wlistentries = wlist->num_widgets + 1;
- wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
- wlistentries * sizeof(struct snd_soc_dapm_widget *);
- *list = krealloc(wlist, wlistsize, GFP_KERNEL);
- if (*list == NULL) {
- dev_err(w->dapm->dev, "ASoC: can't allocate widget list for %s\n",
- w->name);
+ *list = kzalloc(sizeof(**list) + size * sizeof(*w), GFP_KERNEL);
+ if (*list == NULL)
return -ENOMEM;
- }
- wlist = *list;
- /* insert the widget */
- dev_dbg(w->dapm->dev, "ASoC: added %s in widget list pos %d\n",
- w->name, wlist->num_widgets);
+ list_for_each_entry(w, widgets, work_list)
+ (*list)->widgets[i++] = w;
- wlist->widgets[wlist->num_widgets] = w;
- wlist->num_widgets++;
- return 1;
+ (*list)->num_widgets = i;
+
+ return 0;
}
/*
- * Recursively check for a completed path to an active or physically connected
- * output widget. Returns number of complete paths.
+ * Common implementation for is_connected_output_ep() and
+ * is_connected_input_ep(). The function is inlined since the combined size of
+ * the two specialized functions is only marginally larger then the size of the
+ * generic function and at the same time the fast path of the specialized
+ * functions is significantly smaller than the generic function.
*/
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
- struct snd_soc_dapm_widget_list **list)
+static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
+ struct list_head *list, enum snd_soc_dapm_direction dir,
+ int (*fn)(struct snd_soc_dapm_widget *, struct list_head *))
{
+ enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
int con = 0;
- if (widget->outputs >= 0)
- return widget->outputs;
+ if (widget->endpoints[dir] >= 0)
+ return widget->endpoints[dir];
DAPM_UPDATE_STAT(widget, path_checks);
- if (widget->is_sink && widget->connected) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
+ /* do we need to add this widget to the list ? */
+ if (list)
+ list_add_tail(&widget->work_list, list);
+
+ if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
+ widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
+ return widget->endpoints[dir];
}
- list_for_each_entry(path, &widget->sinks, list_source) {
+ snd_soc_dapm_widget_for_each_path(widget, rdir, path) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
if (path->weak || path->is_supply)
@@ -961,91 +1102,40 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
if (path->walking)
return 1;
- trace_snd_soc_dapm_output_path(widget, path);
+ trace_snd_soc_dapm_path(widget, dir, path);
if (path->connect) {
path->walking = 1;
-
- /* do we need to add this widget to the list ? */
- if (list) {
- int err;
- err = dapm_list_add_widget(list, path->sink);
- if (err < 0) {
- dev_err(widget->dapm->dev,
- "ASoC: could not add widget %s\n",
- widget->name);
- path->walking = 0;
- return con;
- }
- }
-
- con += is_connected_output_ep(path->sink, list);
-
+ con += fn(path->node[dir], list);
path->walking = 0;
}
}
- widget->outputs = con;
+ widget->endpoints[dir] = con;
return con;
}
/*
* Recursively check for a completed path to an active or physically connected
+ * output widget. Returns number of complete paths.
+ */
+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+ struct list_head *list)
+{
+ return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
+ is_connected_output_ep);
+}
+
+/*
+ * Recursively check for a completed path to an active or physically connected
* input widget. Returns number of complete paths.
*/
static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
- struct snd_soc_dapm_widget_list **list)
+ struct list_head *list)
{
- struct snd_soc_dapm_path *path;
- int con = 0;
-
- if (widget->inputs >= 0)
- return widget->inputs;
-
- DAPM_UPDATE_STAT(widget, path_checks);
-
- if (widget->is_source && widget->connected) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- list_for_each_entry(path, &widget->sources, list_sink) {
- DAPM_UPDATE_STAT(widget, neighbour_checks);
-
- if (path->weak || path->is_supply)
- continue;
-
- if (path->walking)
- return 1;
-
- trace_snd_soc_dapm_input_path(widget, path);
-
- if (path->connect) {
- path->walking = 1;
-
- /* do we need to add this widget to the list ? */
- if (list) {
- int err;
- err = dapm_list_add_widget(list, path->source);
- if (err < 0) {
- dev_err(widget->dapm->dev,
- "ASoC: could not add widget %s\n",
- widget->name);
- path->walking = 0;
- return con;
- }
- }
-
- con += is_connected_input_ep(path->source, list);
-
- path->walking = 0;
- }
- }
-
- widget->inputs = con;
-
- return con;
+ return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
+ is_connected_input_ep);
}
/**
@@ -1065,7 +1155,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
{
struct snd_soc_card *card = dai->component->card;
struct snd_soc_dapm_widget *w;
+ LIST_HEAD(widgets);
int paths;
+ int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
@@ -1074,14 +1166,21 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
* to reset the cached number of inputs and outputs.
*/
list_for_each_entry(w, &card->widgets, list) {
- w->inputs = -1;
- w->outputs = -1;
+ w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
+ w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
}
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- paths = is_connected_output_ep(dai->playback_widget, list);
+ paths = is_connected_output_ep(dai->playback_widget, &widgets);
else
- paths = is_connected_input_ep(dai->capture_widget, list);
+ paths = is_connected_input_ep(dai->capture_widget, &widgets);
+
+ /* Drop starting point */
+ list_del(widgets.next);
+
+ ret = dapm_widget_list_create(list, &widgets);
+ if (ret)
+ paths = ret;
trace_snd_soc_dapm_connected(paths, stream);
mutex_unlock(&card->dapm_mutex);
@@ -1182,7 +1281,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
/* Check if one of our outputs is connected */
- list_for_each_entry(path, &w->sinks, list_source) {
+ snd_soc_dapm_widget_for_each_sink_path(w, path) {
DAPM_UPDATE_STAT(w, neighbour_checks);
if (path->weak)
@@ -1606,12 +1705,12 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
/* If we changed our power state perhaps our neigbours changed
* also.
*/
- list_for_each_entry(path, &w->sources, list_sink)
+ snd_soc_dapm_widget_for_each_source_path(w, path)
dapm_widget_set_peer_power(path->source, power, path->connect);
/* Supplies can't affect their outputs, only their inputs */
if (!w->is_supply) {
- list_for_each_entry(path, &w->sinks, list_source)
+ snd_soc_dapm_widget_for_each_sink_path(w, path)
dapm_widget_set_peer_power(path->sink, power,
path->connect);
}
@@ -1812,6 +1911,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
{
struct snd_soc_dapm_widget *w = file->private_data;
struct snd_soc_card *card = w->dapm->card;
+ enum snd_soc_dapm_direction dir, rdir;
char *buf;
int in, out;
ssize_t ret;
@@ -1848,25 +1948,21 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->sname,
w->active ? "active" : "inactive");
- list_for_each_entry(p, &w->sources, list_sink) {
- if (p->connected && !p->connected(w, p->source))
- continue;
+ snd_soc_dapm_for_each_direction(dir) {
+ rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ snd_soc_dapm_widget_for_each_path(w, dir, p) {
+ if (p->connected && !p->connected(w, p->node[rdir]))
+ continue;
- if (p->connect)
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
- " in \"%s\" \"%s\"\n",
- p->name ? p->name : "static",
- p->source->name);
- }
- list_for_each_entry(p, &w->sinks, list_source) {
- if (p->connected && !p->connected(w, p->sink))
- continue;
+ if (!p->connect)
+ continue;
- if (p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
- " out \"%s\" \"%s\"\n",
+ " %s \"%s\" \"%s\"\n",
+ (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out",
p->name ? p->name : "static",
- p->sink->name);
+ p->node[rdir]->name);
+ }
}
mutex_unlock(&card->dapm_mutex);
@@ -2084,14 +2180,16 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
-static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf)
+static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
+ char *buf)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
struct snd_soc_dapm_widget *w;
int count = 0;
char *state = "not set";
- list_for_each_entry(w, &codec->component.card->widgets, list) {
- if (w->dapm != &codec->dapm)
+ list_for_each_entry(w, &cmpnt->card->widgets, list) {
+ if (w->dapm != dapm)
continue;
/* only display widgets that burnm power */
@@ -2119,7 +2217,7 @@ static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf)
}
}
- switch (codec->dapm.bias_level) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_ON:
state = "On";
break;
@@ -2148,8 +2246,9 @@ static ssize_t dapm_widget_show(struct device *dev,
mutex_lock(&rtd->card->dapm_mutex);
for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_codec *codec = rtd->codec_dais[i]->codec;
- count += dapm_widget_show_codec(codec, buf + count);
+ struct snd_soc_component *cmpnt = rtd->codec_dais[i]->component;
+
+ count += dapm_widget_show_component(cmpnt, buf + count);
}
mutex_unlock(&rtd->card->dapm_mutex);
@@ -2166,38 +2265,51 @@ struct attribute *soc_dapm_dev_attrs[] = {
static void dapm_free_path(struct snd_soc_dapm_path *path)
{
- list_del(&path->list_sink);
- list_del(&path->list_source);
+ list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]);
+ list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]);
list_del(&path->list_kcontrol);
list_del(&path->list);
kfree(path);
}
+void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p, *next_p;
+ enum snd_soc_dapm_direction dir;
+
+ list_del(&w->list);
+ /*
+ * remove source and sink paths associated to this widget.
+ * While removing the path, remove reference to it from both
+ * source and sink widgets so that path is removed only once.
+ */
+ snd_soc_dapm_for_each_direction(dir) {
+ snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)
+ dapm_free_path(p);
+ }
+
+ kfree(w->kcontrols);
+ kfree_const(w->name);
+ kfree(w);
+}
+
+void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm)
+{
+ dapm->path_sink_cache.widget = NULL;
+ dapm->path_source_cache.widget = NULL;
+}
+
/* free all dapm widgets and resources */
static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_dapm_widget *w, *next_w;
- struct snd_soc_dapm_path *p, *next_p;
list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
if (w->dapm != dapm)
continue;
- list_del(&w->list);
- /*
- * remove source and sink paths associated to this widget.
- * While removing the path, remove reference to it from both
- * source and sink widgets so that path is removed only once.
- */
- list_for_each_entry_safe(p, next_p, &w->sources, list_sink)
- dapm_free_path(p);
-
- list_for_each_entry_safe(p, next_p, &w->sinks, list_source)
- dapm_free_path(p);
-
- kfree(w->kcontrols);
- kfree(w->name);
- kfree(w);
+ snd_soc_dapm_free_widget(w);
}
+ snd_soc_dapm_reset_cache(dapm);
}
static struct snd_soc_dapm_widget *dapm_find_widget(
@@ -2302,20 +2414,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
*/
static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
{
+ enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *p;
+ unsigned int ep;
switch (w->id) {
case snd_soc_dapm_input:
/* On a fully routed card a input is never a source */
if (w->dapm->card->fully_routed)
- break;
- w->is_source = 1;
- list_for_each_entry(p, &w->sources, list_sink) {
+ return;
+ ep = SND_SOC_DAPM_EP_SOURCE;
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
if (p->source->id == snd_soc_dapm_micbias ||
p->source->id == snd_soc_dapm_mic ||
p->source->id == snd_soc_dapm_line ||
p->source->id == snd_soc_dapm_output) {
- w->is_source = 0;
+ ep = 0;
break;
}
}
@@ -2323,25 +2437,74 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
case snd_soc_dapm_output:
/* On a fully routed card a output is never a sink */
if (w->dapm->card->fully_routed)
- break;
- w->is_sink = 1;
- list_for_each_entry(p, &w->sinks, list_source) {
+ return;
+ ep = SND_SOC_DAPM_EP_SINK;
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->sink->id == snd_soc_dapm_spk ||
p->sink->id == snd_soc_dapm_hp ||
p->sink->id == snd_soc_dapm_line ||
p->sink->id == snd_soc_dapm_input) {
- w->is_sink = 0;
+ ep = 0;
break;
}
}
break;
case snd_soc_dapm_line:
- w->is_sink = !list_empty(&w->sources);
- w->is_source = !list_empty(&w->sinks);
+ ep = 0;
+ snd_soc_dapm_for_each_direction(dir) {
+ if (!list_empty(&w->edges[dir]))
+ ep |= SND_SOC_DAPM_DIR_TO_EP(dir);
+ }
+ break;
+ default:
+ return;
+ }
+
+ w->is_ep = ep;
+}
+
+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,
@@ -2350,6 +2513,8 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink))
{
+ struct snd_soc_dapm_widget *widgets[2];
+ enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *path;
int ret;
@@ -2374,17 +2539,22 @@ 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;
- path->source = wsource;
- path->sink = wsink;
+ path->node[SND_SOC_DAPM_DIR_IN] = wsource;
+ path->node[SND_SOC_DAPM_DIR_OUT] = wsink;
+ widgets[SND_SOC_DAPM_DIR_IN] = wsource;
+ widgets[SND_SOC_DAPM_DIR_OUT] = wsink;
+
path->connected = connected;
INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_kcontrol);
- INIT_LIST_HEAD(&path->list_source);
- INIT_LIST_HEAD(&path->list_sink);
if (wsource->is_supply || wsink->is_supply)
path->is_supply = 1;
@@ -2393,10 +2563,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;
@@ -2408,23 +2587,18 @@ 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;
}
}
list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
-
- dapm_update_widget_flags(wsource);
- dapm_update_widget_flags(wsink);
+ snd_soc_dapm_for_each_direction(dir)
+ list_add(&path->list_node[dir], &widgets[dir]->edges[dir]);
- dapm_mark_dirty(wsource, "Route added");
- dapm_mark_dirty(wsink, "Route added");
+ snd_soc_dapm_for_each_direction(dir) {
+ dapm_update_widget_flags(widgets[dir]);
+ dapm_mark_dirty(widgets[dir], "Route added");
+ }
if (dapm->card->instantiated && path->connect)
dapm_path_invalidate(path);
@@ -2460,6 +2634,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
@@ -2467,14 +2647,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 */
@@ -2494,6 +2680,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)
@@ -2656,7 +2846,7 @@ static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n",
route->source, route->sink);
- list_for_each_entry(path, &source->sinks, list_source) {
+ snd_soc_dapm_widget_for_each_sink_path(source, path) {
if (path->sink == sink) {
path->weak = 1;
count++;
@@ -2710,7 +2900,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
/**
* snd_soc_dapm_new_widgets - add new dapm widgets
- * @dapm: DAPM context
+ * @card: card to be checked for new dapm widgets
*
* Checks the codec for any new dapm widgets and creates them if found.
*
@@ -2745,6 +2935,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:
@@ -2911,16 +3102,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);
@@ -2950,7 +3146,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;
@@ -2969,19 +3165,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);
@@ -3062,10 +3259,28 @@ 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)
{
+ enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_widget *w;
const char *prefix;
int ret;
@@ -3112,7 +3327,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
if (prefix)
w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
else
- w->name = kasprintf(GFP_KERNEL, "%s", widget->name);
+ w->name = kstrdup_const(widget->name, GFP_KERNEL);
if (w->name == NULL) {
kfree(w);
return NULL;
@@ -3120,30 +3335,31 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
switch (w->id) {
case snd_soc_dapm_mic:
- w->is_source = 1;
+ w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_input:
if (!dapm->card->fully_routed)
- w->is_source = 1;
+ w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
- w->is_sink = 1;
+ w->is_ep = SND_SOC_DAPM_EP_SINK;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_output:
if (!dapm->card->fully_routed)
- w->is_sink = 1;
+ w->is_ep = SND_SOC_DAPM_EP_SINK;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_vmid:
case snd_soc_dapm_siggen:
- w->is_source = 1;
+ w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_always_on_check_power;
break;
case snd_soc_dapm_mux:
+ case snd_soc_dapm_demux:
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
@@ -3173,14 +3389,14 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
}
w->dapm = dapm;
- INIT_LIST_HEAD(&w->sources);
- INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
- list_add(&w->list, &dapm->card->widgets);
+ list_add_tail(&w->list, &dapm->card->widgets);
- w->inputs = -1;
- w->outputs = -1;
+ snd_soc_dapm_for_each_direction(dir) {
+ INIT_LIST_HEAD(&w->edges[dir]);
+ w->endpoints[dir] = -1;
+ }
/* machine layer set ups unconnected pins and insertions */
w->connected = 1;
@@ -3207,7 +3423,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",
@@ -3234,19 +3450,17 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
int ret;
if (WARN_ON(!config) ||
- WARN_ON(list_empty(&w->sources) || list_empty(&w->sinks)))
+ WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
+ list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])))
return -EINVAL;
/* We only support a single source and sink, pick the first */
- source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path,
- list_sink);
- sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path,
- list_source);
-
- if (WARN_ON(!source_p || !sink_p) ||
- WARN_ON(!sink_p->source || !source_p->sink) ||
- WARN_ON(!source_p->source || !sink_p->sink))
- return -EINVAL;
+ source_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_OUT],
+ struct snd_soc_dapm_path,
+ list_node[SND_SOC_DAPM_DIR_OUT]);
+ sink_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_IN],
+ struct snd_soc_dapm_path,
+ list_node[SND_SOC_DAPM_DIR_IN]);
source = source_p->source->priv;
sink = sink_p->sink->priv;
@@ -3283,11 +3497,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ if (source->driver->ops && source->driver->ops->startup) {
+ ret = source->driver->ops->startup(&substream, source);
+ if (ret < 0) {
+ dev_err(source->dev,
+ "ASoC: startup() failed: %d\n", ret);
+ goto out;
+ }
+ source->active++;
+ }
ret = soc_dai_hw_params(&substream, params, source);
if (ret < 0)
goto out;
substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ if (sink->driver->ops && sink->driver->ops->startup) {
+ ret = sink->driver->ops->startup(&substream, sink);
+ if (ret < 0) {
+ dev_err(sink->dev,
+ "ASoC: startup() failed: %d\n", ret);
+ goto out;
+ }
+ sink->active++;
+ }
ret = soc_dai_hw_params(&substream, params, sink);
if (ret < 0)
goto out;
@@ -3307,11 +3539,23 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret);
ret = 0;
+
+ source->active--;
+ if (source->driver->ops && source->driver->ops->shutdown) {
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ source->driver->ops->shutdown(&substream, source);
+ }
+
+ sink->active--;
+ if (sink->driver->ops && sink->driver->ops->shutdown) {
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ sink->driver->ops->shutdown(&substream, sink);
+ }
break;
default:
WARN(1, "Unknown event %d\n", event);
- return -EINVAL;
+ ret = -EINVAL;
}
out:
@@ -3324,7 +3568,7 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = w->params_select;
+ ucontrol->value.enumerated.item[0] = w->params_select;
return 0;
}
@@ -3338,13 +3582,13 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
if (w->power)
return -EBUSY;
- if (ucontrol->value.integer.value[0] == w->params_select)
+ if (ucontrol->value.enumerated.item[0] == w->params_select)
return 0;
- if (ucontrol->value.integer.value[0] >= w->num_params)
+ if (ucontrol->value.enumerated.item[0] >= w->num_params)
return -EINVAL;
- w->params_select = ucontrol->value.integer.value[0];
+ w->params_select = ucontrol->value.enumerated.item[0];
return 0;
}
@@ -3445,7 +3689,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);
@@ -3496,7 +3740,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);
@@ -3515,7 +3759,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);
@@ -3588,11 +3832,6 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
- /* there is no point in connecting BE DAI links with dummies */
- if (snd_soc_dai_is_dummy(codec_dai) ||
- snd_soc_dai_is_dummy(cpu_dai))
- continue;
-
/* connect BE DAI playback if widgets are valid */
if (codec_dai->playback_widget && cpu_dai->playback_widget) {
source = cpu_dai->playback_widget;
@@ -3623,6 +3862,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
int event)
{
struct snd_soc_dapm_widget *w;
+ unsigned int ep;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
@@ -3632,12 +3872,22 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
if (w) {
dapm_mark_dirty(w, "stream event");
+ if (w->id == snd_soc_dapm_dai_in) {
+ ep = SND_SOC_DAPM_EP_SOURCE;
+ dapm_widget_invalidate_input_paths(w);
+ } else {
+ ep = SND_SOC_DAPM_EP_SINK;
+ dapm_widget_invalidate_output_paths(w);
+ }
+
switch (event) {
case SND_SOC_DAPM_STREAM_START:
w->active = 1;
+ w->is_ep = ep;
break;
case SND_SOC_DAPM_STREAM_STOP:
w->active = 0;
+ w->is_ep = 0;
break;
case SND_SOC_DAPM_STREAM_SUSPEND:
case SND_SOC_DAPM_STREAM_RESUME:
@@ -3645,14 +3895,6 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
break;
}
-
- if (w->id == snd_soc_dapm_dai_in) {
- w->is_source = w->active;
- dapm_widget_invalidate_input_paths(w);
- } else {
- w->is_sink = w->active;
- dapm_widget_invalidate_output_paths(w);
- }
}
}
diff --git a/kernel/sound/soc/soc-generic-dmaengine-pcm.c b/kernel/sound/soc/soc-generic-dmaengine-pcm.c
index c9917ca5d..6fd1906af 100644
--- a/kernel/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/kernel/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/kernel/sound/soc/soc-jack.c b/kernel/sound/soc/soc-jack.c
index 9f60c25c4..fbaa1bb41 100644
--- a/kernel/sound/soc/soc-jack.c
+++ b/kernel/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/kernel/sound/soc/soc-ops.c b/kernel/sound/soc/soc-ops.c
index 100d92b5b..2f67ba6d7 100644
--- a/kernel/sound/soc/soc-ops.c
+++ b/kernel/sound/soc/soc-ops.c
@@ -207,6 +207,34 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
/**
+ * snd_soc_info_volsw_sx - Mixer info callback for SX TLV controls
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers of the SX TLV type. SX TLV controls
+ * have a range that represents both positive and negative values either side
+ * of zero but without a sign bit.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ snd_soc_info_volsw(kcontrol, uinfo);
+ /* Max represents the number of levels in an SX control not the
+ * maximum value, so add the minimum value back on
+ */
+ uinfo->value.integer.max += mc->min;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_sx);
+
+/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
@@ -376,7 +404,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
@@ -560,16 +588,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
- * @codec: where to look for the control
+ * @card: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
+int snd_soc_limit_volume(struct snd_soc_card *card,
const char *name, int max)
{
- struct snd_card *card = codec->component.card->snd_card;
+ struct snd_card *snd_card = card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
int found = 0;
@@ -579,7 +607,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
if (unlikely(!name || max <= 0))
return -EINVAL;
- list_for_each_entry(kctl, &card->controls, list) {
+ list_for_each_entry(kctl, &snd_card->controls, list) {
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
found = 1;
break;
diff --git a/kernel/sound/soc/soc-pcm.c b/kernel/sound/soc/soc-pcm.c
index 35fe58f4f..65b936e25 100644
--- a/kernel/sound/soc/soc-pcm.c
+++ b/kernel/sound/soc/soc-pcm.c
@@ -34,6 +34,24 @@
#define DPCM_MAX_BE_USERS 8
+/*
+ * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
+ *
+ * Returns true if the DAI supports the indicated stream type.
+ */
+static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_pcm_stream *codec_stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &dai->driver->playback;
+ else
+ codec_stream = &dai->driver->capture;
+
+ /* If the codec specifies any rate at all, it supports the stream. */
+ return codec_stream->rates;
+}
+
/**
* snd_soc_runtime_activate() - Increment active count for PCM runtime components
* @rtd: ASoC PCM runtime that is activated
@@ -182,9 +200,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
soc_dai->rate);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- soc_dai->rate, soc_dai->rate);
+ soc_dai->rate);
if (ret < 0) {
dev_err(soc_dai->dev,
"ASoC: Unable to apply rate constraint: %d\n",
@@ -198,9 +216,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
soc_dai->channels);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- soc_dai->channels,
soc_dai->channels);
if (ret < 0) {
dev_err(soc_dai->dev,
@@ -215,9 +232,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
soc_dai->sample_bits);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- soc_dai->sample_bits,
soc_dai->sample_bits);
if (ret < 0) {
dev_err(soc_dai->dev,
@@ -371,6 +387,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
/* first calculate min/max only for CODECs in the DAI link */
for (i = 0; i < rtd->num_codecs; i++) {
+
+ /*
+ * Skip CODECs which don't support the current stream type.
+ * Otherwise, since the rate, channel, and format values will
+ * zero in that case, we would have no usable settings left,
+ * causing the resulting setup to fail.
+ * At least one CODEC should match, otherwise we should have
+ * bailed out on a higher level, since there would be no
+ * CODEC to support the transfer direction in that case.
+ */
+ if (!snd_soc_dai_stream_valid(rtd->codec_dais[i],
+ substream->stream))
+ continue;
+
codec_dai_drv = rtd->codec_dais[i]->driver;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
codec_stream = &codec_dai_drv->playback;
@@ -827,6 +857,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
struct snd_pcm_hw_params codec_params;
+ /*
+ * Skip CODECs which don't support the current stream type,
+ * the idea being that if a CODEC is not used for the currently
+ * set up transfer direction, it should not need to be
+ * configured, especially since the configuration used might
+ * not even be supported by that CODEC. There may be cases
+ * however where a CODEC needs to be set up although it is
+ * actually not being used for the transfer, e.g. if a
+ * capture-only CODEC is acting as an LRCLK and/or BCLK master
+ * for the DAI link including a playback-only CODEC.
+ * If this becomes necessary, we will have to augment the
+ * machine driver setup with information on how to act, so
+ * we can do the right thing here.
+ */
+ if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
+ continue;
+
/* copy params for each codec */
codec_params = *params;
@@ -1231,24 +1278,17 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
}
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
- int stream, struct snd_soc_dapm_widget_list **list_)
+ int stream, struct snd_soc_dapm_widget_list **list)
{
struct snd_soc_dai *cpu_dai = fe->cpu_dai;
- struct snd_soc_dapm_widget_list *list;
int paths;
- list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) +
- sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL);
- if (list == NULL)
- return -ENOMEM;
-
/* get number of valid DAI paths and their widgets */
- paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list);
+ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list);
dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
stream ? "capture" : "playback");
- *list_ = list;
return paths;
}
@@ -1306,7 +1346,12 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
switch (list->widgets[i]->id) {
case snd_soc_dapm_dai_in:
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ continue;
+ break;
case snd_soc_dapm_dai_out:
+ if (stream != SNDRV_PCM_STREAM_CAPTURE)
+ continue;
break;
default:
continue;
@@ -1485,30 +1530,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);
@@ -1661,7 +1743,8 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
continue;
dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
diff --git a/kernel/sound/soc/soc-topology.c b/kernel/sound/soc/soc-topology.c
new file mode 100644
index 000000000..6963ba209
--- /dev/null
+++ b/kernel/sound/soc/soc-topology.c
@@ -0,0 +1,1860 @@
+/*
+ * 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>
+#include <sound/tlv.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_PCM_DAI 4
+#define SOC_TPLG_PASS_GRAPH 5
+#define SOC_TPLG_PASS_PINS 6
+
+#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST
+#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS
+
+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 */
+
+ /* vendor specific kcontrol operations */
+ const struct snd_soc_tplg_kcontrol_ops *io_ops;
+ int io_ops_count;
+
+ /* vendor specific bytes ext handlers, for TLV bytes controls */
+ const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops;
+ int bytes_ext_ops_count;
+
+ /* optional fw loading callbacks to component drivers */
+ struct snd_soc_tplg_ops *ops;
+};
+
+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, snd_soc_info_volsw},
+ {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 soc_tplg *tplg)
+{
+ const struct snd_soc_tplg_kcontrol_ops *ops;
+ const struct snd_soc_tplg_bytes_ext_ops *ext_ops;
+ int num_ops, i;
+
+ if (hdr->ops.info == SND_SOC_TPLG_CTL_BYTES
+ && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER
+ && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE
+ && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ struct soc_bytes_ext *sbe;
+ struct snd_soc_tplg_bytes_control *be;
+
+ sbe = (struct soc_bytes_ext *)k->private_value;
+ be = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
+
+ /* TLV bytes controls need standard kcontrol info handler,
+ * TLV callback and extended put/get handlers.
+ */
+ k->info = snd_soc_bytes_info_ext;
+ k->tlv.c = snd_soc_bytes_tlv_callback;
+
+ ext_ops = tplg->bytes_ext_ops;
+ num_ops = tplg->bytes_ext_ops_count;
+ for (i = 0; i < num_ops; i++) {
+ if (!sbe->put && ext_ops[i].id == be->ext_ops.put)
+ sbe->put = ext_ops[i].put;
+ if (!sbe->get && ext_ops[i].id == be->ext_ops.get)
+ sbe->get = ext_ops[i].get;
+ }
+
+ if (sbe->put && sbe->get)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ /* try and map vendor specific kcontrol handlers first */
+ ops = tplg->io_ops;
+ num_ops = tplg->io_ops_count;
+ for (i = 0; i < num_ops; i++) {
+
+ if (k->put == NULL && ops[i].id == hdr->ops.put)
+ k->put = ops[i].put;
+ if (k->get == NULL && ops[i].id == hdr->ops.get)
+ k->get = ops[i].get;
+ if (k->info == NULL && ops[i].id == hdr->ops.info)
+ k->info = ops[i].info;
+ }
+
+ /* vendor specific handlers found ? */
+ if (k->put && k->get && k->info)
+ return 0;
+
+ /* none found so try standard kcontrol handlers */
+ ops = io_ops;
+ num_ops = ARRAY_SIZE(io_ops);
+ for (i = 0; i < num_ops; i++) {
+
+ if (k->put == NULL && ops[i].id == hdr->ops.put)
+ k->put = ops[i].put;
+ if (k->get == NULL && ops[i].id == hdr->ops.get)
+ k->get = ops[i].get;
+ if (k->info == NULL && ops[i].id == hdr->ops.info)
+ k->info = ops[i].info;
+ }
+
+ /* standard 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_db_scale(struct soc_tplg *tplg,
+ struct snd_kcontrol_new *kc, struct snd_soc_tplg_tlv_dbscale *scale)
+{
+ unsigned int item_len = 2 * sizeof(unsigned int);
+ unsigned int *p;
+
+ p = kzalloc(item_len + 2 * sizeof(unsigned int), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = SNDRV_CTL_TLVT_DB_SCALE;
+ p[1] = item_len;
+ p[2] = scale->min;
+ p[3] = (scale->step & TLV_DB_SCALE_MASK)
+ | (scale->mute ? TLV_DB_SCALE_MUTE : 0);
+
+ kc->tlv.p = (void *)p;
+ return 0;
+}
+
+static int soc_tplg_create_tlv(struct soc_tplg *tplg,
+ struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc)
+{
+ struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+
+ if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE))
+ return 0;
+
+ if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
+ tplg_tlv = &tc->tlv;
+ switch (tplg_tlv->type) {
+ case SNDRV_CTL_TLVT_DB_SCALE:
+ return soc_tplg_create_tlv_db_scale(tplg, kc,
+ &tplg_tlv->scale);
+
+ /* TODO: add support for other TLV types */
+ default:
+ dev_dbg(tplg->dev, "Unsupported TLV type %d\n",
+ tplg_tlv->type);
+ return -EINVAL;
+ }
+ }
+
+ 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, tplg);
+ 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, tplg);
+ 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);
+
+ /* 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 = kmemdup(ec->values,
+ ec->items * sizeof(u32),
+ GFP_KERNEL);
+ if (!se->dobj.control.dvalues)
+ return -ENOMEM;
+
+ 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, tplg);
+ 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 = kcalloc(num_kcontrols, sizeof(*kc), 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], tplg);
+ 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, tplg);
+ 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 = kcalloc(count, sizeof(*kc), 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);
+
+ 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], tplg);
+ 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.subseq = w->subseq;
+ 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), 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) * 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;
+ tplg.bytes_ext_ops = ops->bytes_ext_ops;
+ tplg.bytes_ext_ops_count = ops->bytes_ext_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;
+
+ 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;
+ /* check and free and dynamic widget kcontrols */
+ snd_soc_tplg_widget_remove(w);
+ snd_soc_dapm_free_widget(w);
+ }
+ snd_soc_dapm_reset_cache(dapm);
+}
+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/kernel/sound/soc/soc-utils.c b/kernel/sound/soc/soc-utils.c
index 362c69ac1..53dd085d3 100644
--- a/kernel/sound/soc/soc-utils.c
+++ b/kernel/sound/soc/soc-utils.c
@@ -101,6 +101,15 @@ static struct snd_soc_codec_driver dummy_codec;
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+/*
+ * The dummy CODEC is only meant to be used in situations where there is no
+ * actual hardware.
+ *
+ * If there is actual hardware even if it does not have a control bus
+ * the hardware will still have constraints like supported samplerates, etc.
+ * which should be modelled. And the data flow graph also should be modelled
+ * using DAPM.
+ */
static struct snd_soc_dai_driver dummy_dai = {
.name = "snd-soc-dummy-dai",
.playback = {
diff --git a/kernel/sound/soc/spear/Kconfig b/kernel/sound/soc/spear/Kconfig
index 0a5305349..4fb91412e 100644
--- a/kernel/sound/soc/spear/Kconfig
+++ b/kernel/sound/soc/spear/Kconfig
@@ -1,6 +1,6 @@
config SND_SPEAR_SOC
tristate
- select SND_DMAENGINE_PCM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
config SND_SPEAR_SPDIF_OUT
tristate
diff --git a/kernel/sound/soc/spear/spdif_in.c b/kernel/sound/soc/spear/spdif_in.c
index a4028601d..977a078eb 100644
--- a/kernel/sound/soc/spear/spdif_in.c
+++ b/kernel/sound/soc/spear/spdif_in.c
@@ -203,35 +203,25 @@ static int spdif_in_probe(struct platform_device *pdev)
struct spdif_in_dev *host;
struct spear_spdif_platform_data *pdata;
struct resource *res, *res_fifo;
+ void __iomem *io_base;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
+ io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res_fifo)
return -EINVAL;
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name)) {
- dev_warn(&pdev->dev, "Failed to get memory resourse\n");
- return -ENOENT;
- }
-
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) {
dev_warn(&pdev->dev, "kzalloc fail\n");
return -ENOMEM;
}
- host->io_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!host->io_base) {
- dev_warn(&pdev->dev, "ioremap failed\n");
- return -ENOMEM;
- }
-
+ host->io_base = io_base;
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0)
return -EINVAL;
diff --git a/kernel/sound/soc/spear/spear_pcm.c b/kernel/sound/soc/spear/spear_pcm.c
index a7dc3c56f..e8476da15 100644
--- a/kernel/sound/soc/spear/spear_pcm.c
+++ b/kernel/sound/soc/spear/spear_pcm.c
@@ -44,7 +44,7 @@ int devm_spear_pcm_platform_register(struct device *dev,
*config = spear_dmaengine_pcm_config;
config->compat_filter_fn = filter;
- return snd_dmaengine_pcm_register(dev, config,
+ return devm_snd_dmaengine_pcm_register(dev, config,
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
diff --git a/kernel/sound/soc/sti/Kconfig b/kernel/sound/soc/sti/Kconfig
new file mode 100644
index 000000000..64a690077
--- /dev/null
+++ b/kernel/sound/soc/sti/Kconfig
@@ -0,0 +1,11 @@
+#
+# STM SoC audio configuration
+#
+menuconfig SND_SOC_STI
+ tristate "SoC Audio support for STI System-On-Chip"
+ depends on SND_SOC
+ depends on ARCH_STI || COMPILE_TEST
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y if you want to enable ASoC-support for
+ any of the STI platforms (e.g. STIH416).
diff --git a/kernel/sound/soc/sti/Makefile b/kernel/sound/soc/sti/Makefile
new file mode 100644
index 000000000..4b188d2d7
--- /dev/null
+++ b/kernel/sound/soc/sti/Makefile
@@ -0,0 +1,4 @@
+# STI platform support
+snd-soc-sti-objs := sti_uniperif.o uniperif_player.o uniperif_reader.o
+
+obj-$(CONFIG_SND_SOC_STI) += snd-soc-sti.o
diff --git a/kernel/sound/soc/sti/sti_uniperif.c b/kernel/sound/soc/sti/sti_uniperif.c
new file mode 100644
index 000000000..39bcefe5e
--- /dev/null
+++ b/kernel/sound/soc/sti/sti_uniperif.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "uniperif.h"
+
+/*
+ * sti_uniperiph_dai_create_ctrl
+ * This function is used to create Ctrl associated to DAI but also pcm device.
+ * Request is done by front end to associate ctrl with pcm device id
+ */
+static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *uni = priv->dai_data.uni;
+ struct snd_kcontrol_new *ctrl;
+ int i;
+
+ if (!uni->num_ctrls)
+ return 0;
+
+ for (i = 0; i < uni->num_ctrls; i++) {
+ /*
+ * Several Control can have same name. Controls are indexed on
+ * Uniperipheral instance ID
+ */
+ ctrl = &uni->snd_ctrls[i];
+ ctrl->index = uni->info->id;
+ ctrl->device = uni->info->id;
+ }
+
+ return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
+}
+
+/*
+ * DAI
+ */
+int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ int transfer_size;
+
+ transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
+
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+ dma_data->maxburst = transfer_size;
+
+ return 0;
+}
+
+int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ priv->dai_data.uni->daifmt = fmt;
+
+ return 0;
+}
+
+static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *uni = priv->dai_data.uni;
+ int ret;
+
+ /* The uniperipheral should be in stopped state */
+ if (uni->state != UNIPERIF_STATE_STOPPED) {
+ dev_err(uni->dev, "%s: invalid uni state( %d)",
+ __func__, (int)uni->state);
+ return -EBUSY;
+ }
+
+ /* Pinctrl: switch pinstate to sleep */
+ ret = pinctrl_pm_select_sleep_state(uni->dev);
+ if (ret)
+ dev_err(uni->dev, "%s: failed to select pinctrl state",
+ __func__);
+
+ return ret;
+}
+
+static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *uni = priv->dai_data.uni;
+ int ret;
+
+ if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) {
+ ret = uni_player_resume(uni);
+ if (ret)
+ return ret;
+ }
+
+ /* pinctrl: switch pinstate to default */
+ ret = pinctrl_pm_select_default_state(uni->dev);
+ if (ret)
+ dev_err(uni->dev, "%s: failed to select pinctrl state",
+ __func__);
+
+ return ret;
+}
+
+static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct sti_uniperiph_dai *dai_data = &priv->dai_data;
+
+ /* DMA settings*/
+ if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player"))
+ snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
+ else
+ snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
+
+ dai_data->dma_data.addr = dai_data->uni->fifo_phys_address;
+ dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ return sti_uniperiph_dai_create_ctrl(dai);
+}
+
+static const struct snd_soc_dai_driver sti_uniperiph_dai_template = {
+ .probe = sti_uniperiph_dai_probe,
+ .suspend = sti_uniperiph_dai_suspend,
+ .resume = sti_uniperiph_dai_resume
+};
+
+static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
+ .name = "sti_cpu_dai",
+};
+
+static int sti_uniperiph_cpu_dai_of(struct device_node *node,
+ struct sti_uniperiph_data *priv)
+{
+ const char *str;
+ int ret;
+ struct device *dev = &priv->pdev->dev;
+ struct sti_uniperiph_dai *dai_data = &priv->dai_data;
+ struct snd_soc_dai_driver *dai = priv->dai;
+ struct snd_soc_pcm_stream *stream;
+ struct uniperif *uni;
+
+ uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
+ if (!uni)
+ return -ENOMEM;
+
+ *dai = sti_uniperiph_dai_template;
+ ret = of_property_read_string(node, "dai-name", &str);
+ if (ret < 0) {
+ dev_err(dev, "%s: dai name missing.\n", __func__);
+ return -EINVAL;
+ }
+ dai->name = str;
+
+ /* Get resources */
+ uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
+
+ if (!uni->mem_region) {
+ dev_err(dev, "Failed to get memory resource");
+ return -ENODEV;
+ }
+
+ uni->base = devm_ioremap_resource(dev, uni->mem_region);
+
+ if (IS_ERR(uni->base))
+ return PTR_ERR(uni->base);
+
+ uni->fifo_phys_address = uni->mem_region->start +
+ UNIPERIF_FIFO_DATA_OFFSET(uni);
+
+ uni->irq = platform_get_irq(priv->pdev, 0);
+ if (uni->irq < 0) {
+ dev_err(dev, "Failed to get IRQ resource");
+ return -ENXIO;
+ }
+
+ dai_data->uni = uni;
+
+ if (of_device_is_compatible(node, "st,sti-uni-player")) {
+ uni_player_init(priv->pdev, uni);
+ stream = &dai->playback;
+ } else {
+ uni_reader_init(priv->pdev, uni);
+ stream = &dai->capture;
+ }
+ dai->ops = uni->dai_ops;
+
+ stream->stream_name = dai->name;
+ stream->channels_min = uni->hw->channels_min;
+ stream->channels_max = uni->hw->channels_max;
+ stream->rates = uni->hw->rates;
+ stream->formats = uni->hw->formats;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static int sti_uniperiph_probe(struct platform_device *pdev)
+{
+ struct sti_uniperiph_data *priv;
+ struct device_node *node = pdev->dev.of_node;
+ int ret;
+
+ /* Allocate the private data and the CPU_DAI array */
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL);
+ if (!priv->dai)
+ return -ENOMEM;
+
+ priv->pdev = pdev;
+
+ ret = sti_uniperiph_cpu_dai_of(node, priv);
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sti_uniperiph_dai_component,
+ priv->dai, 1);
+ if (ret < 0)
+ return ret;
+
+ return devm_snd_dmaengine_pcm_register(&pdev->dev,
+ &dmaengine_pcm_config, 0);
+}
+
+static const struct of_device_id snd_soc_sti_match[] = {
+ { .compatible = "st,sti-uni-player", },
+ { .compatible = "st,sti-uni-reader", },
+ {},
+};
+
+static struct platform_driver sti_uniperiph_driver = {
+ .driver = {
+ .name = "sti-uniperiph-dai",
+ .of_match_table = snd_soc_sti_match,
+ },
+ .probe = sti_uniperiph_probe,
+};
+module_platform_driver(sti_uniperiph_driver);
+
+MODULE_DESCRIPTION("uniperipheral DAI driver");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/sti/uniperif.h b/kernel/sound/soc/sti/uniperif.h
new file mode 100644
index 000000000..f0fd5a994
--- /dev/null
+++ b/kernel/sound/soc/sti/uniperif.h
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef __SND_ST_AUD_UNIPERIF_H
+#define __SND_ST_AUD_UNIPERIF_H
+
+#include <linux/regmap.h>
+
+#include <sound/dmaengine_pcm.h>
+
+/*
+ * Register access macros
+ */
+
+#define GET_UNIPERIF_REG(ip, offset, shift, mask) \
+ ((readl_relaxed(ip->base + offset) >> shift) & mask)
+#define SET_UNIPERIF_REG(ip, offset, shift, mask, value) \
+ writel_relaxed(((readl_relaxed(ip->base + offset) & \
+ ~(mask << shift)) | (((value) & mask) << shift)), ip->base + offset)
+#define SET_UNIPERIF_BIT_REG(ip, offset, shift, mask, value) \
+ writel_relaxed((((value) & mask) << shift), ip->base + offset)
+
+/*
+ * AUD_UNIPERIF_SOFT_RST reg
+ */
+
+#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000
+#define GET_UNIPERIF_SOFT_RST(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ readl_relaxed(ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) : 0)
+#define SET_UNIPERIF_SOFT_RST(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_SOFT_RST_OFFSET(ip))
+
+/* SOFT_RST */
+#define UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip) 0x0
+#define UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip) 0x1
+#define SET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \
+ SET_UNIPERIF_BIT_REG(ip, \
+ UNIPERIF_SOFT_RST_OFFSET(ip), \
+ UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \
+ UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip), 1)
+#define GET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_SOFT_RST_OFFSET(ip), \
+ UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \
+ UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip))
+
+/*
+ * AUD_UNIPERIF_FIFO_DATA reg
+ */
+
+#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004
+#define SET_UNIPERIF_DATA(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip))
+
+/*
+ * AUD_UNIPERIF_CHANNEL_STA_REGN reg
+ */
+
+#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
+#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n))
+#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \
+ writel_relaxed(value, ip->base + \
+ UNIPERIF_CHANNEL_STA_REGN(ip, n))
+
+#define UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip) 0x0060
+#define GET_UNIPERIF_CHANNEL_STA_REG0(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip))
+#define SET_UNIPERIF_CHANNEL_STA_REG0(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip))
+
+#define UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip) 0x0064
+#define GET_UNIPERIF_CHANNEL_STA_REG1(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip))
+#define SET_UNIPERIF_CHANNEL_STA_REG1(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip))
+
+#define UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip) 0x0068
+#define GET_UNIPERIF_CHANNEL_STA_REG2(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip))
+#define SET_UNIPERIF_CHANNEL_STA_REG2(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip))
+
+#define UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip) 0x006C
+#define GET_UNIPERIF_CHANNEL_STA_REG3(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip))
+#define SET_UNIPERIF_CHANNEL_STA_REG3(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip))
+
+#define UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip) 0x0070
+#define GET_UNIPERIF_CHANNEL_STA_REG4(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip))
+#define SET_UNIPERIF_CHANNEL_STA_REG4(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip))
+
+#define UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip) 0x0074
+#define GET_UNIPERIF_CHANNEL_STA_REG5(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip))
+#define SET_UNIPERIF_CHANNEL_STA_REG5(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip))
+
+/*
+ * AUD_UNIPERIF_ITS reg
+ */
+
+#define UNIPERIF_ITS_OFFSET(ip) 0x000C
+#define GET_UNIPERIF_ITS(ip) \
+ readl_relaxed(ip->base + UNIPERIF_ITS_OFFSET(ip))
+
+/* MEM_BLK_READ */
+#define UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip) 5
+#define UNIPERIF_ITS_MEM_BLK_READ_MASK(ip) \
+ (BIT(UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip)))
+
+/* FIFO_ERROR */
+#define UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8)
+#define UNIPERIF_ITS_FIFO_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip)))
+
+/* DMA_ERROR */
+#define UNIPERIF_ITS_DMA_ERROR_SHIFT(ip) 9
+#define UNIPERIF_ITS_DMA_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITS_DMA_ERROR_SHIFT(ip)))
+
+/* UNDERFLOW_REC_DONE */
+#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12)
+#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip))))
+
+/* UNDERFLOW_REC_FAILED */
+#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13)
+#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip))))
+
+/*
+ * AUD_UNIPERIF_ITS_BCLR reg
+ */
+
+/* FIFO_ERROR */
+#define UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8)
+#define UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip)))
+#define SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(ip) \
+ SET_UNIPERIF_ITS_BCLR(ip, \
+ UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip))
+
+#define UNIPERIF_ITS_BCLR_OFFSET(ip) 0x0010
+#define SET_UNIPERIF_ITS_BCLR(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip))
+
+/*
+ * AUD_UNIPERIF_ITM reg
+ */
+
+#define UNIPERIF_ITM_OFFSET(ip) 0x0018
+#define GET_UNIPERIF_ITM(ip) \
+ readl_relaxed(ip->base + UNIPERIF_ITM_OFFSET(ip))
+
+/* FIFO_ERROR */
+#define UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8)
+#define UNIPERIF_ITM_FIFO_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip)))
+
+/* UNDERFLOW_REC_DONE */
+#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12)
+#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip))))
+
+/* UNDERFLOW_REC_FAILED */
+#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13)
+#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip))))
+
+/*
+ * AUD_UNIPERIF_ITM_BCLR reg
+ */
+
+#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c
+#define SET_UNIPERIF_ITM_BCLR(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_ITM_BCLR_OFFSET(ip))
+
+/* FIFO_ERROR */
+#define UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8)
+#define UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip)))
+#define SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(ip) \
+ SET_UNIPERIF_ITM_BCLR(ip, \
+ UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip))
+
+/* DMA_ERROR */
+#define UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip) 9
+#define UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip)))
+#define SET_UNIPERIF_ITM_BCLR_DMA_ERROR(ip) \
+ SET_UNIPERIF_ITM_BCLR(ip, \
+ UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip))
+
+/*
+ * AUD_UNIPERIF_ITM_BSET reg
+ */
+
+#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020
+#define SET_UNIPERIF_ITM_BSET(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_ITM_BSET_OFFSET(ip))
+
+/* FIFO_ERROR */
+#define UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8)
+#define UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip)))
+#define SET_UNIPERIF_ITM_BSET_FIFO_ERROR(ip) \
+ SET_UNIPERIF_ITM_BSET(ip, \
+ UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip))
+
+/* MEM_BLK_READ */
+#define UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip) 5
+#define UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip) \
+ (BIT(UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip)))
+#define SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(ip) \
+ SET_UNIPERIF_ITM_BSET(ip, \
+ UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip))
+
+/* DMA_ERROR */
+#define UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip) 9
+#define UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip) \
+ (BIT(UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip)))
+#define SET_UNIPERIF_ITM_BSET_DMA_ERROR(ip) \
+ SET_UNIPERIF_ITM_BSET(ip, \
+ UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip))
+
+/* UNDERFLOW_REC_DONE */
+#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12)
+#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip))))
+#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(ip) \
+ SET_UNIPERIF_ITM_BSET(ip, \
+ UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip))
+
+/* UNDERFLOW_REC_FAILED */
+#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13)
+#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \
+ 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip))))
+#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(ip) \
+ SET_UNIPERIF_ITM_BSET(ip, \
+ UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip))
+
+/*
+ * UNIPERIF_CONFIG reg
+ */
+
+#define UNIPERIF_CONFIG_OFFSET(ip) 0x0040
+#define GET_UNIPERIF_CONFIG(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CONFIG_OFFSET(ip))
+#define SET_UNIPERIF_CONFIG(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CONFIG_OFFSET(ip))
+
+/* PARITY_CNTR */
+#define UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip) 0
+#define UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_PARITY_CNTR(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip))
+#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_SW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 1)
+
+/* CHANNEL_STA_CNTR */
+#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip) 1
+#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip))
+#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_SW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 1)
+
+/* USER_DAT_CNTR */
+#define UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip) 2
+#define UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_USER_DAT_CNTR(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip))
+#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 1)
+#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_SW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 0)
+
+/* VALIDITY_DAT_CNTR */
+#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip) 3
+#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip))
+#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_SW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \
+ UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 1)
+
+/* ONE_BIT_AUD_SUPPORT */
+#define UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip) 4
+#define UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_ONE_BIT_AUD(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \
+ UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip))
+#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \
+ UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_ENABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \
+ UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 1)
+
+/* MEMORY_FMT */
+#define UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip) 5
+#define UNIPERIF_CONFIG_MEM_FMT_MASK(ip) 0x1
+#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) 0
+#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) 1
+#define GET_UNIPERIF_CONFIG_MEM_FMT(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \
+ UNIPERIF_CONFIG_MEM_FMT_MASK(ip))
+#define SET_UNIPERIF_CONFIG_MEM_FMT(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \
+ UNIPERIF_CONFIG_MEM_FMT_MASK(ip), value)
+#define SET_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) \
+ SET_UNIPERIF_CONFIG_MEM_FMT(ip, \
+ VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip))
+#define SET_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) \
+ SET_UNIPERIF_CONFIG_MEM_FMT(ip, \
+ VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip))
+
+/* REPEAT_CHL_STS */
+#define UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip) 6
+#define UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_REPEAT_CHL_STS(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \
+ UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip))
+#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \
+ UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_DISABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \
+ UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 1)
+
+/* BACK_STALL_REQ */
+#define UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 7 : -1)
+#define UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_BACK_STALL_REQ(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \
+ UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip))
+#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \
+ UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_ENABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \
+ UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 1)
+
+/* FDMA_TRIGGER_LIMIT */
+#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip) 8
+#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip) 0x7F
+#define GET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \
+ UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip))
+#define SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \
+ UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip), value)
+
+/* CHL_STS_UPDATE */
+#define UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1)
+#define UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \
+ UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip))
+#define SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \
+ UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip), 1)
+
+/* IDLE_MOD */
+#define UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip) 18
+#define UNIPERIF_CONFIG_IDLE_MOD_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_IDLE_MOD(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \
+ UNIPERIF_CONFIG_IDLE_MOD_MASK(ip))
+#define SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \
+ UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 0)
+#define SET_UNIPERIF_CONFIG_IDLE_MOD_ENABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \
+ UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 1)
+
+/* SUBFRAME_SELECTION */
+#define UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip) 19
+#define UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_SUBFRAME_SEL(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \
+ UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip))
+#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \
+ UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 1)
+#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF0_SUBF1(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \
+ UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 0)
+
+/* FULL_SW_CONTROL */
+#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip) 20
+#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_SPDIF_SW_CTRL(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \
+ UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip))
+#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_ENABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \
+ UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 1)
+#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \
+ UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 0)
+
+/* MASTER_CLKEDGE */
+#define UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 24 : -1)
+#define UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip) 0x1
+#define GET_UNIPERIF_CONFIG_MSTR_CLKEDGE(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \
+ UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip))
+#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_FALLING(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \
+ UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 1)
+#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_RISING(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CONFIG_OFFSET(ip), \
+ UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \
+ UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 0)
+
+/*
+ * UNIPERIF_CTRL reg
+ */
+
+#define UNIPERIF_CTRL_OFFSET(ip) 0x0044
+#define GET_UNIPERIF_CTRL(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CTRL_OFFSET(ip))
+#define SET_UNIPERIF_CTRL(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_CTRL_OFFSET(ip))
+
+/* OPERATION */
+#define UNIPERIF_CTRL_OPERATION_SHIFT(ip) 0
+#define UNIPERIF_CTRL_OPERATION_MASK(ip) 0x7
+#define GET_UNIPERIF_CTRL_OPERATION(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip) 0
+#define SET_UNIPERIF_CTRL_OPERATION_OFF(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 1 : -1)
+#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 2 : -1)
+#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) 3
+#define SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip))
+/* This is the same as above! */
+#define VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) 3
+#define SET_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) 4
+#define SET_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 5 : -1)
+#define SET_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip))
+#define VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 7)
+#define SET_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_OPERATION_SHIFT(ip), \
+ UNIPERIF_CTRL_OPERATION_MASK(ip), \
+ VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip))
+
+/* EXIT_STBY_ON_EOBLOCK */
+#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 3)
+#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \
+ UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip))
+#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \
+ UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 0)
+#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \
+ UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 1)
+
+/* ROUNDING */
+#define UNIPERIF_CTRL_ROUNDING_SHIFT(ip) 4
+#define UNIPERIF_CTRL_ROUNDING_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_ROUNDING(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \
+ UNIPERIF_CTRL_ROUNDING_MASK(ip))
+#define SET_UNIPERIF_CTRL_ROUNDING_OFF(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \
+ UNIPERIF_CTRL_ROUNDING_MASK(ip), 0)
+#define SET_UNIPERIF_CTRL_ROUNDING_ON(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \
+ UNIPERIF_CTRL_ROUNDING_MASK(ip), 1)
+
+/* DIVIDER */
+#define UNIPERIF_CTRL_DIVIDER_SHIFT(ip) 5
+#define UNIPERIF_CTRL_DIVIDER_MASK(ip) 0xff
+#define GET_UNIPERIF_CTRL_DIVIDER(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \
+ UNIPERIF_CTRL_DIVIDER_MASK(ip))
+#define SET_UNIPERIF_CTRL_DIVIDER(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \
+ UNIPERIF_CTRL_DIVIDER_MASK(ip), value)
+
+/* BYTE_SWAP */
+#define UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 13 : -1)
+#define UNIPERIF_CTRL_BYTE_SWP_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_BYTE_SWP(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \
+ UNIPERIF_CTRL_BYTE_SWP_MASK(ip))
+#define SET_UNIPERIF_CTRL_BYTE_SWP_OFF(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \
+ UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 0)
+#define SET_UNIPERIF_CTRL_BYTE_SWP_ON(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \
+ UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 1)
+
+/* ZERO_STUFFING_HW_SW */
+#define UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 14 : -1)
+#define UNIPERIF_CTRL_ZERO_STUFF_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_ZERO_STUFF(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \
+ UNIPERIF_CTRL_ZERO_STUFF_MASK(ip))
+#define SET_UNIPERIF_CTRL_ZERO_STUFF_HW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \
+ UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 1)
+#define SET_UNIPERIF_CTRL_ZERO_STUFF_SW(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \
+ UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 0)
+
+/* SPDIF_LAT */
+#define UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1)
+#define UNIPERIF_CTRL_SPDIF_LAT_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_SPDIF_LAT(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \
+ UNIPERIF_CTRL_SPDIF_LAT_MASK(ip))
+#define SET_UNIPERIF_CTRL_SPDIF_LAT_ON(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \
+ UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 1)
+#define SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \
+ UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 0)
+
+/* EN_SPDIF_FORMATTING */
+#define UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip) 17
+#define UNIPERIF_CTRL_SPDIF_FMT_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_SPDIF_FMT(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \
+ UNIPERIF_CTRL_SPDIF_FMT_MASK(ip))
+#define SET_UNIPERIF_CTRL_SPDIF_FMT_ON(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \
+ UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 1)
+#define SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \
+ UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 0)
+
+/* READER_OUT_SELECT */
+#define UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 18 : -1)
+#define UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip) 0x1
+#define GET_UNIPERIF_CTRL_READER_OUT_SEL(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \
+ UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip))
+#define SET_UNIPERIF_CTRL_READER_OUT_SEL_IN_MEM(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \
+ UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 0)
+#define SET_UNIPERIF_CTRL_READER_OUT_SEL_ON_I2S_LINE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \
+ CORAUD_UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
+
+/* UNDERFLOW_REC_WINDOW */
+#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20
+#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip) 0xff
+#define GET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \
+ UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip))
+#define SET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_CTRL_OFFSET(ip), \
+ UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \
+ UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip), value)
+
+/*
+ * UNIPERIF_I2S_FMT a.k.a UNIPERIF_FORMAT reg
+ */
+
+#define UNIPERIF_I2S_FMT_OFFSET(ip) 0x0048
+#define GET_UNIPERIF_I2S_FMT(ip) \
+ readl_relaxed(ip->base + UNIPERIF_I2S_FMT_OFFSET(ip))
+#define SET_UNIPERIF_I2S_FMT(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_I2S_FMT_OFFSET(ip))
+
+/* NBIT */
+#define UNIPERIF_I2S_FMT_NBIT_SHIFT(ip) 0
+#define UNIPERIF_I2S_FMT_NBIT_MASK(ip) 0x1
+#define GET_UNIPERIF_I2S_FMT_NBIT(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NBIT_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_NBIT_32(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NBIT_MASK(ip), 0)
+#define SET_UNIPERIF_I2S_FMT_NBIT_16(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NBIT_MASK(ip), 1)
+
+/* DATA_SIZE */
+#define UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip) 1
+#define UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip) 0x7
+#define GET_UNIPERIF_I2S_FMT_DATA_SIZE(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 0)
+#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_18(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 1)
+#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_20(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 2)
+#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 3)
+#define SET_UNIPERIF_I2S_FMTL_DATA_SIZE_28(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 4)
+#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 5)
+
+/* LR_POL */
+#define UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip) 4
+#define UNIPERIF_I2S_FMT_LR_POL_MASK(ip) 0x1
+#define VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) 0x0
+#define VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) 0x1
+#define GET_UNIPERIF_I2S_FMT_LR_POL(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_LR_POL_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_LR_POL(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_LR_POL_MASK(ip), value)
+#define SET_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) \
+ SET_UNIPERIF_I2S_FMT_LR_POL(ip, \
+ VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip))
+#define SET_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) \
+ SET_UNIPERIF_I2S_FMT_LR_POL(ip, \
+ VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip))
+
+/* SCLK_EDGE */
+#define UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip) 5
+#define UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip) 0x1
+#define GET_UNIPERIF_I2S_FMT_SCLK_EDGE(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 0)
+#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 1)
+
+/* PADDING */
+#define UNIPERIF_I2S_FMT_PADDING_SHIFT(ip) 6
+#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1
+#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1
+#define VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) 0x0
+#define VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) 0x1
+#define GET_UNIPERIF_I2S_FMT_PADDING(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_PADDING_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_PADDING(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_PADDING_MASK(ip), value)
+#define SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) \
+ SET_UNIPERIF_I2S_FMT_PADDING(ip, \
+ VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip))
+#define SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) \
+ SET_UNIPERIF_I2S_FMT_PADDING(ip, \
+ VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip))
+
+/* ALIGN */
+#define UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip) 7
+#define UNIPERIF_I2S_FMT_ALIGN_MASK(ip) 0x1
+#define GET_UNIPERIF_I2S_FMT_ALIGN(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_ALIGN_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 0)
+#define SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 1)
+
+/* ORDER */
+#define UNIPERIF_I2S_FMT_ORDER_SHIFT(ip) 8
+#define UNIPERIF_I2S_FMT_ORDER_MASK(ip) 0x1
+#define GET_UNIPERIF_I2S_FMT_ORDER(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_ORDER_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_ORDER_LSB(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_ORDER_MASK(ip), 0)
+#define SET_UNIPERIF_I2S_FMT_ORDER_MSB(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_ORDER_MASK(ip), 1)
+
+/* NUM_CH */
+#define UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip) 9
+#define UNIPERIF_I2S_FMT_NUM_CH_MASK(ip) 0x7
+#define GET_UNIPERIF_I2S_FMT_NUM_CH(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NUM_CH_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_NUM_CH(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NUM_CH_MASK(ip), value)
+
+/* NO_OF_SAMPLES_TO_READ */
+#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip) 12
+#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip) 0xfffff
+#define GET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip))
+#define SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_I2S_FMT_OFFSET(ip), \
+ UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \
+ UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip), value)
+
+/*
+ * UNIPERIF_BIT_CONTROL reg
+ */
+
+#define UNIPERIF_BIT_CONTROL_OFFSET(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0x004c)
+#define GET_UNIPERIF_BIT_CONTROL(ip) \
+ readl_relaxed(ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip))
+#define SET_UNIPERIF_BIT_CONTROL(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip))
+
+/* CLR_UNDERFLOW_DURATION */
+#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip) 0
+#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip) 0x1
+#define GET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_BIT_CONTROL_OFFSET(ip), \
+ UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \
+ UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip))
+#define SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_BIT_CONTROL_OFFSET(ip), \
+ UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \
+ UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip), 1)
+
+/* CHL_STS_UPDATE */
+#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip) 1
+#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip) 0x1
+#define GET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_BIT_CONTROL_OFFSET(ip), \
+ UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \
+ UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip))
+#define SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \
+ SET_UNIPERIF_BIT_REG(ip, \
+ UNIPERIF_BIT_CONTROL_OFFSET(ip), \
+ UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \
+ UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip), 1)
+
+/*
+ * UNIPERIF_STATUS_1 reg
+ */
+
+#define UNIPERIF_STATUS_1_OFFSET(ip) 0x0050
+#define GET_UNIPERIF_STATUS_1(ip) \
+ readl_relaxed(ip->base + UNIPERIF_STATUS_1_OFFSET(ip))
+#define SET_UNIPERIF_STATUS_1(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_STATUS_1_OFFSET(ip))
+
+/* UNDERFLOW_DURATION */
+#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0)
+#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip) 0xff
+#define GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_STATUS_1_OFFSET(ip), \
+ UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \
+ UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip))
+#define SET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_STATUS_1_OFFSET(ip), \
+ UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \
+ UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value)
+
+/*
+ * AUD_UNIPERIF_CHANNEL_STA_REGN reg
+ */
+
+#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
+#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \
+ readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n))
+#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \
+ writel_relaxed(value, ip->base + \
+ UNIPERIF_CHANNEL_STA_REGN(ip, n))
+
+/*
+ * AUD_UNIPERIF_USER_VALIDITY reg
+ */
+
+#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090
+#define GET_UNIPERIF_USER_VALIDITY(ip) \
+ readl_relaxed(ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip))
+#define SET_UNIPERIF_USER_VALIDITY(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip))
+
+/* VALIDITY_LEFT_AND_RIGHT */
+#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip) 0
+#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip) 0x3
+#define GET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_USER_VALIDITY_OFFSET(ip), \
+ UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \
+ UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip))
+#define SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_USER_VALIDITY_OFFSET(ip), \
+ UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \
+ UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip), \
+ value ? 0x3 : 0)
+
+/*
+ * UNIPERIF_DBG_STANDBY_LEFT_SP reg
+ */
+#define UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip) 0x0150
+#define UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0)
+#define UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip) \
+ ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 0xFFFFFF)
+#define GET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \
+ UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \
+ UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip))
+#define SET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \
+ UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \
+ UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value)
+
+/*
+ * uniperipheral IP capabilities
+ */
+
+#define UNIPERIF_FIFO_SIZE 70 /* FIFO is 70 cells deep */
+#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */
+
+/*
+ * Uniperipheral IP revisions
+ */
+enum uniperif_version {
+ SND_ST_UNIPERIF_VERSION_UNKNOWN,
+ /* SASG1 (Orly), Newman */
+ SND_ST_UNIPERIF_VERSION_C6AUD0_UNI_1_0,
+ /* SASC1, SASG2 (Orly2) */
+ SND_ST_UNIPERIF_VERSION_UNI_PLR_1_0,
+ /* SASC1, SASG2 (Orly2), TELSS, Cannes */
+ SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
+ /* TELSS (SASC1) */
+ SND_ST_UNIPERIF_VERSION_TDM_PLR_1_0,
+ /* Cannes/Monaco */
+ SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
+};
+
+enum uniperif_type {
+ SND_ST_UNIPERIF_PLAYER_TYPE_NONE,
+ SND_ST_UNIPERIF_PLAYER_TYPE_HDMI,
+ SND_ST_UNIPERIF_PLAYER_TYPE_PCM,
+ SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF
+};
+
+enum uniperif_state {
+ UNIPERIF_STATE_STOPPED,
+ UNIPERIF_STATE_STARTED,
+ UNIPERIF_STATE_STANDBY,
+ UNIPERIF_STATE_UNDERFLOW,
+ UNIPERIF_STATE_OVERFLOW = UNIPERIF_STATE_UNDERFLOW,
+ UNIPERIF_STATE_XRUN
+};
+
+enum uniperif_iec958_encoding_mode {
+ UNIPERIF_IEC958_ENCODING_MODE_PCM,
+ UNIPERIF_IEC958_ENCODING_MODE_ENCODED
+};
+
+struct uniperif_info {
+ int id; /* instance value of the uniperipheral IP */
+ enum uniperif_type player_type;
+ int underflow_enabled; /* Underflow recovery mode */
+};
+
+struct uniperif_iec958_settings {
+ enum uniperif_iec958_encoding_mode encoding_mode;
+ struct snd_aes_iec958 iec958;
+};
+
+struct uniperif {
+ /* System information */
+ struct uniperif_info *info;
+ struct device *dev;
+ int ver; /* IP version, used by register access macros */
+ struct regmap_field *clk_sel;
+
+ /* capabilities */
+ const struct snd_pcm_hardware *hw;
+
+ /* Resources */
+ struct resource *mem_region;
+ void __iomem *base;
+ unsigned long fifo_phys_address;
+ int irq;
+
+ /* Clocks */
+ struct clk *clk;
+ int mclk;
+ int clk_adj;
+
+ /* Runtime data */
+ enum uniperif_state state;
+
+ struct snd_pcm_substream *substream;
+
+ /* Specific to IEC958 player */
+ struct uniperif_iec958_settings stream_settings;
+ struct mutex ctrl_lock; /* For resource updated by stream and controls*/
+
+ /*alsa ctrl*/
+ struct snd_kcontrol_new *snd_ctrls;
+ int num_ctrls;
+
+ /* dai properties */
+ unsigned int daifmt;
+
+ /* DAI callbacks */
+ const struct snd_soc_dai_ops *dai_ops;
+};
+
+struct sti_uniperiph_dai {
+ int stream;
+ struct uniperif *uni;
+ struct snd_dmaengine_dai_dma_data dma_data;
+};
+
+struct sti_uniperiph_data {
+ struct platform_device *pdev;
+ struct snd_soc_dai_driver *dai;
+ struct sti_uniperiph_dai dai_data;
+};
+
+/* uniperiph player*/
+int uni_player_init(struct platform_device *pdev,
+ struct uniperif *uni_player);
+int uni_player_resume(struct uniperif *player);
+
+/* uniperiph reader */
+int uni_reader_init(struct platform_device *pdev,
+ struct uniperif *uni_reader);
+
+/* common */
+int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt);
+
+int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+
+#endif
diff --git a/kernel/sound/soc/sti/uniperif_player.c b/kernel/sound/soc/sti/uniperif_player.c
new file mode 100644
index 000000000..5c2bc53f0
--- /dev/null
+++ b/kernel/sound/soc/sti/uniperif_player.c
@@ -0,0 +1,1119 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+
+#include "uniperif.h"
+
+/*
+ * Some hardware-related definitions
+ */
+
+/* sys config registers definitions */
+#define SYS_CFG_AUDIO_GLUE 0xA4
+#define SYS_CFG_AUDI0_GLUE_PCM_CLKX 8
+
+/*
+ * Driver specific types.
+ */
+#define UNIPERIF_PLAYER_TYPE_IS_HDMI(p) \
+ ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_HDMI)
+#define UNIPERIF_PLAYER_TYPE_IS_PCM(p) \
+ ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_PCM)
+#define UNIPERIF_PLAYER_TYPE_IS_SPDIF(p) \
+ ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF)
+#define UNIPERIF_PLAYER_TYPE_IS_IEC958(p) \
+ (UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \
+ UNIPERIF_PLAYER_TYPE_IS_SPDIF(p))
+
+#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999
+#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000
+
+/*
+ * Note: snd_pcm_hardware is linked to DMA controller but is declared here to
+ * integrate DAI_CPU capability in term of rate and supported channels
+ */
+static const struct snd_pcm_hardware uni_player_pcm_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+
+ .channels_min = 2,
+ .channels_max = 8,
+
+ .periods_min = 2,
+ .periods_max = 48,
+
+ .period_bytes_min = 128,
+ .period_bytes_max = 64 * PAGE_SIZE,
+ .buffer_bytes_max = 256 * PAGE_SIZE
+};
+
+static inline int reset_player(struct uniperif *player)
+{
+ int count = 10;
+
+ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
+ while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) {
+ udelay(5);
+ count--;
+ }
+ }
+
+ if (!count) {
+ dev_err(player->dev, "Failed to reset uniperif");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * uni_player_irq_handler
+ * In case of error audio stream is stopped; stop action is protected via PCM
+ * stream lock to avoid race condition with trigger callback.
+ */
+static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
+{
+ irqreturn_t ret = IRQ_NONE;
+ struct uniperif *player = dev_id;
+ unsigned int status;
+ unsigned int tmp;
+
+ if (player->state == UNIPERIF_STATE_STOPPED) {
+ /* Unexpected IRQ: do nothing */
+ return IRQ_NONE;
+ }
+
+ /* Get interrupt status & clear them immediately */
+ status = GET_UNIPERIF_ITS(player);
+ SET_UNIPERIF_ITS_BCLR(player, status);
+
+ /* Check for fifo error (underrun) */
+ if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) {
+ dev_err(player->dev, "FIFO underflow error detected");
+
+ /* Interrupt is just for information when underflow recovery */
+ if (player->info->underflow_enabled) {
+ /* Update state to underflow */
+ player->state = UNIPERIF_STATE_UNDERFLOW;
+
+ } else {
+ /* Disable interrupt so doesn't continually fire */
+ SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player);
+
+ /* Stop the player */
+ snd_pcm_stream_lock(player->substream);
+ snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock(player->substream);
+ }
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* Check for dma error (overrun) */
+ if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) {
+ dev_err(player->dev, "DMA error detected");
+
+ /* Disable interrupt so doesn't continually fire */
+ SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player);
+
+ /* Stop the player */
+ snd_pcm_stream_lock(player->substream);
+ snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock(player->substream);
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* Check for underflow recovery done */
+ if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) {
+ if (!player->info->underflow_enabled) {
+ dev_err(player->dev, "unexpected Underflow recovering");
+ return -EPERM;
+ }
+ /* Read the underflow recovery duration */
+ tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player);
+
+ /* Clear the underflow recovery duration */
+ SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player);
+
+ /* Update state to started */
+ player->state = UNIPERIF_STATE_STARTED;
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* Check if underflow recovery failed */
+ if (unlikely(status &
+ UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) {
+ dev_err(player->dev, "Underflow recovery failed");
+
+ /* Stop the player */
+ snd_pcm_stream_lock(player->substream);
+ snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock(player->substream);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int uni_player_clk_set_rate(struct uniperif *player, unsigned long rate)
+{
+ int rate_adjusted, rate_achieved, delta, ret;
+ int adjustment = player->clk_adj;
+
+ /*
+ * a
+ * F = f + --------- * f = f + d
+ * 1000000
+ *
+ * a
+ * d = --------- * f
+ * 1000000
+ *
+ * where:
+ * f - nominal rate
+ * a - adjustment in ppm (parts per milion)
+ * F - rate to be set in synthesizer
+ * d - delta (difference) between f and F
+ */
+ if (adjustment < 0) {
+ /* div64_64 operates on unsigned values... */
+ delta = -1;
+ adjustment = -adjustment;
+ } else {
+ delta = 1;
+ }
+ /* 500000 ppm is 0.5, which is used to round up values */
+ delta *= (int)div64_u64((uint64_t)rate *
+ (uint64_t)adjustment + 500000, 1000000);
+ rate_adjusted = rate + delta;
+
+ /* Adjusted rate should never be == 0 */
+ if (!rate_adjusted)
+ return -EINVAL;
+
+ ret = clk_set_rate(player->clk, rate_adjusted);
+ if (ret < 0)
+ return ret;
+
+ rate_achieved = clk_get_rate(player->clk);
+ if (!rate_achieved)
+ /* If value is 0 means that clock or parent not valid */
+ return -EINVAL;
+
+ /*
+ * Using ALSA's adjustment control, we can modify the rate to be up
+ * to twice as much as requested, but no more
+ */
+ delta = rate_achieved - rate;
+ if (delta < 0) {
+ /* div64_64 operates on unsigned values... */
+ delta = -delta;
+ adjustment = -1;
+ } else {
+ adjustment = 1;
+ }
+ /* Frequency/2 is added to round up result */
+ adjustment *= (int)div64_u64((uint64_t)delta * 1000000 + rate / 2,
+ rate);
+ player->clk_adj = adjustment;
+ return 0;
+}
+
+static void uni_player_set_channel_status(struct uniperif *player,
+ struct snd_pcm_runtime *runtime)
+{
+ int n;
+ unsigned int status;
+
+ /*
+ * Some AVRs and TVs require the channel status to contain a correct
+ * sampling frequency. If no sample rate is already specified, then
+ * set one.
+ */
+ mutex_lock(&player->ctrl_lock);
+ if (runtime && (player->stream_settings.iec958.status[3]
+ == IEC958_AES3_CON_FS_NOTID)) {
+ switch (runtime->rate) {
+ case 22050:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_22050;
+ break;
+ case 44100:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_44100;
+ break;
+ case 88200:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_88200;
+ break;
+ case 176400:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_176400;
+ break;
+ case 24000:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_24000;
+ break;
+ case 48000:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_48000;
+ break;
+ case 96000:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_96000;
+ break;
+ case 192000:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_192000;
+ break;
+ case 32000:
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_32000;
+ break;
+ default:
+ /* Mark as sampling frequency not indicated */
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_NOTID;
+ break;
+ }
+ }
+
+ /* Audio mode:
+ * Use audio mode status to select PCM or encoded mode
+ */
+ if (player->stream_settings.iec958.status[0] & IEC958_AES0_NONAUDIO)
+ player->stream_settings.encoding_mode =
+ UNIPERIF_IEC958_ENCODING_MODE_ENCODED;
+ else
+ player->stream_settings.encoding_mode =
+ UNIPERIF_IEC958_ENCODING_MODE_PCM;
+
+ if (player->stream_settings.encoding_mode ==
+ UNIPERIF_IEC958_ENCODING_MODE_PCM)
+ /* Clear user validity bits */
+ SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0);
+ else
+ /* Set user validity bits */
+ SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 1);
+
+ /* Program the new channel status */
+ for (n = 0; n < 6; ++n) {
+ status =
+ player->stream_settings.iec958.status[0 + (n * 4)] & 0xf;
+ status |=
+ player->stream_settings.iec958.status[1 + (n * 4)] << 8;
+ status |=
+ player->stream_settings.iec958.status[2 + (n * 4)] << 16;
+ status |=
+ player->stream_settings.iec958.status[3 + (n * 4)] << 24;
+ SET_UNIPERIF_CHANNEL_STA_REGN(player, n, status);
+ }
+ mutex_unlock(&player->ctrl_lock);
+
+ /* Update the channel status */
+ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player);
+ else
+ SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player);
+}
+
+static int uni_player_prepare_iec958(struct uniperif *player,
+ struct snd_pcm_runtime *runtime)
+{
+ int clk_div;
+
+ clk_div = player->mclk / runtime->rate;
+
+ /* Oversampling must be multiple of 128 as iec958 frame is 32-bits */
+ if ((clk_div % 128) || (clk_div <= 0)) {
+ dev_err(player->dev, "%s: invalid clk_div %d",
+ __func__, clk_div);
+ return -EINVAL;
+ }
+
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /* 16/16 memory format */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player);
+ /* 16-bits per sub-frame */
+ SET_UNIPERIF_I2S_FMT_NBIT_32(player);
+ /* Set 16-bit sample precision */
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /* 16/0 memory format */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
+ /* 32-bits per sub-frame */
+ SET_UNIPERIF_I2S_FMT_NBIT_32(player);
+ /* Set 24-bit sample precision */
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player);
+ break;
+ default:
+ dev_err(player->dev, "format not supported");
+ return -EINVAL;
+ }
+
+ /* Set parity to be calculated by the hardware */
+ SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(player);
+
+ /* Set channel status bits to be inserted by the hardware */
+ SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(player);
+
+ /* Set user data bits to be inserted by the hardware */
+ SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(player);
+
+ /* Set validity bits to be inserted by the hardware */
+ SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(player);
+
+ /* Set full software control to disabled */
+ SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(player);
+
+ SET_UNIPERIF_CTRL_ZERO_STUFF_HW(player);
+
+ /* Update the channel status */
+ uni_player_set_channel_status(player, runtime);
+
+ /* Clear the user validity user bits */
+ SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0);
+
+ /* Disable one-bit audio mode */
+ SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player);
+
+ /* Enable consecutive frames repetition of Z preamble (not for HBRA) */
+ SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(player);
+
+ /* Change to SUF0_SUBF1 and left/right channels swap! */
+ SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(player);
+
+ /* Set data output as MSB first */
+ SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
+
+ if (player->stream_settings.encoding_mode ==
+ UNIPERIF_IEC958_ENCODING_MODE_ENCODED)
+ SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(player);
+ else
+ SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(player);
+
+ SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2);
+
+ /* Set rounding to off */
+ SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
+
+ /* Set clock divisor */
+ SET_UNIPERIF_CTRL_DIVIDER(player, clk_div / 128);
+
+ /* Set the spdif latency to not wait before starting player */
+ SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
+
+ /*
+ * Ensure iec958 formatting is off. It will be enabled in function
+ * uni_player_start() at the same time as the operation
+ * mode is set to work around a silicon issue.
+ */
+ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player);
+ else
+ SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
+
+ return 0;
+}
+
+static int uni_player_prepare_pcm(struct uniperif *player,
+ struct snd_pcm_runtime *runtime)
+{
+ int output_frame_size, slot_width, clk_div;
+
+ /* Force slot width to 32 in I2S mode (HW constraint) */
+ if ((player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+ SND_SOC_DAIFMT_I2S) {
+ slot_width = 32;
+ } else {
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ slot_width = 16;
+ break;
+ default:
+ slot_width = 32;
+ break;
+ }
+ }
+ output_frame_size = slot_width * runtime->channels;
+
+ clk_div = player->mclk / runtime->rate;
+ /*
+ * For 32 bits subframe clk_div must be a multiple of 128,
+ * for 16 bits must be a multiple of 64
+ */
+ if ((slot_width == 32) && (clk_div % 128)) {
+ dev_err(player->dev, "%s: invalid clk_div", __func__);
+ return -EINVAL;
+ }
+
+ if ((slot_width == 16) && (clk_div % 64)) {
+ dev_err(player->dev, "%s: invalid clk_div", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Number of bits per subframe (which is one channel sample)
+ * on output - Transfer 16 or 32 bits from FIFO
+ */
+ switch (slot_width) {
+ case 32:
+ SET_UNIPERIF_I2S_FMT_NBIT_32(player);
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player);
+ break;
+ case 16:
+ SET_UNIPERIF_I2S_FMT_NBIT_16(player);
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player);
+ break;
+ default:
+ dev_err(player->dev, "subframe format not supported");
+ return -EINVAL;
+ }
+
+ /* Configure data memory format */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /* One data word contains two samples */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /*
+ * Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits
+ * on the left than zeros (if less than 32 bytes)"... ;-)
+ */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
+ break;
+
+ default:
+ dev_err(player->dev, "format not supported");
+ return -EINVAL;
+ }
+
+ /* Set rounding to off */
+ SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
+
+ /* Set clock divisor */
+ SET_UNIPERIF_CTRL_DIVIDER(player, clk_div / (2 * output_frame_size));
+
+ /* Number of channelsmust be even*/
+ if ((runtime->channels % 2) || (runtime->channels < 2) ||
+ (runtime->channels > 10)) {
+ dev_err(player->dev, "%s: invalid nb of channels", __func__);
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2);
+
+ /* Set 1-bit audio format to disabled */
+ SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player);
+
+ SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
+
+ /* No iec958 formatting as outputting to DAC */
+ SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player);
+
+ return 0;
+}
+
+/*
+ * ALSA uniperipheral iec958 controls
+ */
+static int uni_player_ctl_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int uni_player_ctl_iec958_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+ struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958;
+
+ mutex_lock(&player->ctrl_lock);
+ ucontrol->value.iec958.status[0] = iec958->status[0];
+ ucontrol->value.iec958.status[1] = iec958->status[1];
+ ucontrol->value.iec958.status[2] = iec958->status[2];
+ ucontrol->value.iec958.status[3] = iec958->status[3];
+ mutex_unlock(&player->ctrl_lock);
+ return 0;
+}
+
+static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+ struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958;
+
+ mutex_lock(&player->ctrl_lock);
+ iec958->status[0] = ucontrol->value.iec958.status[0];
+ iec958->status[1] = ucontrol->value.iec958.status[1];
+ iec958->status[2] = ucontrol->value.iec958.status[2];
+ iec958->status[3] = ucontrol->value.iec958.status[3];
+ mutex_unlock(&player->ctrl_lock);
+
+ uni_player_set_channel_status(player, NULL);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new uni_player_iec958_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = uni_player_ctl_iec958_info,
+ .get = uni_player_ctl_iec958_get,
+ .put = uni_player_ctl_iec958_put,
+};
+
+/*
+ * uniperif rate adjustement control
+ */
+static int snd_sti_clk_adjustment_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = UNIPERIF_PLAYER_CLK_ADJ_MIN;
+ uinfo->value.integer.max = UNIPERIF_PLAYER_CLK_ADJ_MAX;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+
+ mutex_lock(&player->ctrl_lock);
+ ucontrol->value.integer.value[0] = player->clk_adj;
+ mutex_unlock(&player->ctrl_lock);
+
+ return 0;
+}
+
+static int snd_sti_clk_adjustment_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+ int ret = 0;
+
+ if ((ucontrol->value.integer.value[0] < UNIPERIF_PLAYER_CLK_ADJ_MIN) ||
+ (ucontrol->value.integer.value[0] > UNIPERIF_PLAYER_CLK_ADJ_MAX))
+ return -EINVAL;
+
+ mutex_lock(&player->ctrl_lock);
+ player->clk_adj = ucontrol->value.integer.value[0];
+
+ if (player->mclk)
+ ret = uni_player_clk_set_rate(player, player->mclk);
+ mutex_unlock(&player->ctrl_lock);
+
+ return ret;
+}
+
+static struct snd_kcontrol_new uni_player_clk_adj_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Playback Oversampling Freq. Adjustment",
+ .info = snd_sti_clk_adjustment_info,
+ .get = snd_sti_clk_adjustment_get,
+ .put = snd_sti_clk_adjustment_put,
+};
+
+static struct snd_kcontrol_new *snd_sti_pcm_ctl[] = {
+ &uni_player_clk_adj_ctl,
+};
+
+static struct snd_kcontrol_new *snd_sti_iec_ctl[] = {
+ &uni_player_iec958_ctl,
+ &uni_player_clk_adj_ctl,
+};
+
+static int uni_player_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+ player->substream = substream;
+
+ player->clk_adj = 0;
+
+ return 0;
+}
+
+static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+ int ret;
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ if (clk_id != 0)
+ return -EINVAL;
+
+ mutex_lock(&player->ctrl_lock);
+ ret = uni_player_clk_set_rate(player, freq);
+ if (!ret)
+ player->mclk = freq;
+ mutex_unlock(&player->ctrl_lock);
+
+ return ret;
+}
+
+static int uni_player_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int transfer_size, trigger_limit;
+ int ret;
+
+ /* The player should be stopped */
+ if (player->state != UNIPERIF_STATE_STOPPED) {
+ dev_err(player->dev, "%s: invalid player state %d", __func__,
+ player->state);
+ return -EINVAL;
+ }
+
+ /* Calculate transfer size (in fifo cells and bytes) for frame count */
+ transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
+
+ /* Calculate number of empty cells available before asserting DREQ */
+ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
+ trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
+ } else {
+ /*
+ * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
+ * FDMA_TRIGGER_LIMIT also controls when the state switches
+ * from OFF or STANDBY to AUDIO DATA.
+ */
+ trigger_limit = transfer_size;
+ }
+
+ /* Trigger limit must be an even number */
+ if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) ||
+ (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) {
+ dev_err(player->dev, "invalid trigger limit %d", trigger_limit);
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);
+
+ /* Uniperipheral setup depends on player type */
+ switch (player->info->player_type) {
+ case SND_ST_UNIPERIF_PLAYER_TYPE_HDMI:
+ ret = uni_player_prepare_iec958(player, runtime);
+ break;
+ case SND_ST_UNIPERIF_PLAYER_TYPE_PCM:
+ ret = uni_player_prepare_pcm(player, runtime);
+ break;
+ case SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF:
+ ret = uni_player_prepare_iec958(player, runtime);
+ break;
+ default:
+ dev_err(player->dev, "invalid player type");
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ switch (player->daifmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
+ break;
+ }
+
+ switch (player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
+ SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(player);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
+ SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(player);
+ SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player);
+ break;
+ default:
+ dev_err(player->dev, "format not supported");
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0);
+
+ /* Reset uniperipheral player */
+ SET_UNIPERIF_SOFT_RST_SOFT_RST(player);
+
+ return reset_player(player);
+}
+
+static int uni_player_start(struct uniperif *player)
+{
+ int ret;
+
+ /* The player should be stopped */
+ if (player->state != UNIPERIF_STATE_STOPPED) {
+ dev_err(player->dev, "%s: invalid player state", __func__);
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(player->clk);
+ if (ret) {
+ dev_err(player->dev, "%s: Failed to enable clock", __func__);
+ return ret;
+ }
+
+ /* Clear any pending interrupts */
+ SET_UNIPERIF_ITS_BCLR(player, GET_UNIPERIF_ITS(player));
+
+ /* Set the interrupt mask */
+ SET_UNIPERIF_ITM_BSET_DMA_ERROR(player);
+ SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player);
+
+ /* Enable underflow recovery interrupts */
+ if (player->info->underflow_enabled) {
+ SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player);
+ SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player);
+ }
+
+ /* Reset uniperipheral player */
+ SET_UNIPERIF_SOFT_RST_SOFT_RST(player);
+
+ ret = reset_player(player);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Does not use IEC61937 features of the uniperipheral hardware.
+ * Instead it performs IEC61937 in software and inserts it directly
+ * into the audio data stream. As such, when encoded mode is selected,
+ * linear pcm mode is still used, but with the differences of the
+ * channel status bits set for encoded mode and the validity bits set.
+ */
+ SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(player);
+
+ /*
+ * If iec958 formatting is required for hdmi or spdif, then it must be
+ * enabled after the operation mode is set. If set prior to this, it
+ * will not take affect and hang the player.
+ */
+ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player))
+ SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
+
+ /* Force channel status update (no update if clk disable) */
+ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player);
+ else
+ SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player);
+
+ /* Update state to started */
+ player->state = UNIPERIF_STATE_STARTED;
+
+ return 0;
+}
+
+static int uni_player_stop(struct uniperif *player)
+{
+ int ret;
+
+ /* The player should not be in stopped state */
+ if (player->state == UNIPERIF_STATE_STOPPED) {
+ dev_err(player->dev, "%s: invalid player state", __func__);
+ return -EINVAL;
+ }
+
+ /* Turn the player off */
+ SET_UNIPERIF_CTRL_OPERATION_OFF(player);
+
+ /* Soft reset the player */
+ SET_UNIPERIF_SOFT_RST_SOFT_RST(player);
+
+ ret = reset_player(player);
+ if (ret < 0)
+ return ret;
+
+ /* Disable interrupts */
+ SET_UNIPERIF_ITM_BCLR(player, GET_UNIPERIF_ITM(player));
+
+ /* Disable clock */
+ clk_disable_unprepare(player->clk);
+
+ /* Update state to stopped and return */
+ player->state = UNIPERIF_STATE_STOPPED;
+
+ return 0;
+}
+
+int uni_player_resume(struct uniperif *player)
+{
+ int ret;
+
+ /* Select the frequency synthesizer clock */
+ if (player->clk_sel) {
+ ret = regmap_field_write(player->clk_sel, 1);
+ if (ret) {
+ dev_err(player->dev,
+ "%s: Failed to select freq synth clock",
+ __func__);
+ return ret;
+ }
+ }
+
+ SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player);
+ SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
+ SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
+ SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uni_player_resume);
+
+static int uni_player_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ return uni_player_start(player);
+ case SNDRV_PCM_TRIGGER_STOP:
+ return uni_player_stop(player);
+ case SNDRV_PCM_TRIGGER_RESUME:
+ return uni_player_resume(player);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void uni_player_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *player = priv->dai_data.uni;
+
+ if (player->state != UNIPERIF_STATE_STOPPED)
+ /* Stop the player */
+ uni_player_stop(player);
+
+ player->substream = NULL;
+}
+
+static int uni_player_parse_dt_clk_glue(struct platform_device *pdev,
+ struct uniperif *player)
+{
+ int bit_offset;
+ struct device_node *node = pdev->dev.of_node;
+ struct regmap *regmap;
+
+ bit_offset = SYS_CFG_AUDI0_GLUE_PCM_CLKX + player->info->id;
+
+ regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
+
+ if (regmap) {
+ struct reg_field regfield =
+ REG_FIELD(SYS_CFG_AUDIO_GLUE, bit_offset, bit_offset);
+
+ player->clk_sel = regmap_field_alloc(regmap, regfield);
+ } else {
+ dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int uni_player_parse_dt(struct platform_device *pdev,
+ struct uniperif *player)
+{
+ struct uniperif_info *info;
+ struct device *dev = &pdev->dev;
+ struct device_node *pnode = pdev->dev.of_node;
+ const char *mode;
+
+ /* Allocate memory for the info structure */
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (of_property_read_u32(pnode, "st,version", &player->ver) ||
+ player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
+ dev_err(dev, "Unknown uniperipheral version ");
+ return -EINVAL;
+ }
+ /* Underflow recovery is only supported on later ip revisions */
+ if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ info->underflow_enabled = 1;
+
+ if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) {
+ dev_err(dev, "uniperipheral id not defined");
+ return -EINVAL;
+ }
+
+ /* Read the device mode property */
+ if (of_property_read_string(pnode, "st,mode", &mode)) {
+ dev_err(dev, "uniperipheral mode not defined");
+ return -EINVAL;
+ }
+
+ if (strcasecmp(mode, "hdmi") == 0)
+ info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI;
+ else if (strcasecmp(mode, "pcm") == 0)
+ info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_PCM;
+ else if (strcasecmp(mode, "spdif") == 0)
+ info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF;
+ else
+ info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_NONE;
+
+ /* Save the info structure */
+ player->info = info;
+
+ /* Get the PCM_CLK_SEL bit from audio-glue-ctrl SoC register */
+ if (uni_player_parse_dt_clk_glue(pdev, player))
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops uni_player_dai_ops = {
+ .startup = uni_player_startup,
+ .shutdown = uni_player_shutdown,
+ .prepare = uni_player_prepare,
+ .trigger = uni_player_trigger,
+ .hw_params = sti_uniperiph_dai_hw_params,
+ .set_fmt = sti_uniperiph_dai_set_fmt,
+ .set_sysclk = uni_player_set_sysclk
+};
+
+int uni_player_init(struct platform_device *pdev,
+ struct uniperif *player)
+{
+ int ret = 0;
+
+ player->dev = &pdev->dev;
+ player->state = UNIPERIF_STATE_STOPPED;
+ player->hw = &uni_player_pcm_hw;
+ player->dai_ops = &uni_player_dai_ops;
+
+ ret = uni_player_parse_dt(pdev, player);
+
+ if (ret < 0) {
+ dev_err(player->dev, "Failed to parse DeviceTree");
+ return ret;
+ }
+
+ /* Get uniperif resource */
+ player->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (IS_ERR(player->clk))
+ ret = PTR_ERR(player->clk);
+
+ /* Select the frequency synthesizer clock */
+ if (player->clk_sel) {
+ ret = regmap_field_write(player->clk_sel, 1);
+ if (ret) {
+ dev_err(player->dev,
+ "%s: Failed to select freq synth clock",
+ __func__);
+ return ret;
+ }
+ }
+
+ ret = devm_request_irq(&pdev->dev, player->irq,
+ uni_player_irq_handler, IRQF_SHARED,
+ dev_name(&pdev->dev), player);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&player->ctrl_lock);
+
+ /* Ensure that disabled by default */
+ SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player);
+ SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
+ SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
+ SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player);
+
+ if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) {
+ /* Set default iec958 status bits */
+
+ /* Consumer, PCM, copyright, 2ch, mode 0 */
+ player->stream_settings.iec958.status[0] = 0x00;
+ /* Broadcast reception category */
+ player->stream_settings.iec958.status[1] =
+ IEC958_AES1_CON_GENERAL;
+ /* Do not take into account source or channel number */
+ player->stream_settings.iec958.status[2] =
+ IEC958_AES2_CON_SOURCE_UNSPEC;
+ /* Sampling frequency not indicated */
+ player->stream_settings.iec958.status[3] =
+ IEC958_AES3_CON_FS_NOTID;
+ /* Max sample word 24-bit, sample word length not indicated */
+ player->stream_settings.iec958.status[4] =
+ IEC958_AES4_CON_MAX_WORDLEN_24 |
+ IEC958_AES4_CON_WORDLEN_24_20;
+
+ player->num_ctrls = ARRAY_SIZE(snd_sti_iec_ctl);
+ player->snd_ctrls = snd_sti_iec_ctl[0];
+ } else {
+ player->num_ctrls = ARRAY_SIZE(snd_sti_pcm_ctl);
+ player->snd_ctrls = snd_sti_pcm_ctl[0];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uni_player_init);
diff --git a/kernel/sound/soc/sti/uniperif_reader.c b/kernel/sound/soc/sti/uniperif_reader.c
new file mode 100644
index 000000000..8a0eb2050
--- /dev/null
+++ b/kernel/sound/soc/sti/uniperif_reader.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <sound/soc.h>
+
+#include "uniperif.h"
+
+/*
+ * Note: snd_pcm_hardware is linked to DMA controller but is declared here to
+ * integrate unireader capability in term of rate and supported channels
+ */
+static const struct snd_pcm_hardware uni_reader_pcm_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 96000,
+
+ .channels_min = 2,
+ .channels_max = 8,
+
+ .periods_min = 2,
+ .periods_max = 48,
+
+ .period_bytes_min = 128,
+ .period_bytes_max = 64 * PAGE_SIZE,
+ .buffer_bytes_max = 256 * PAGE_SIZE
+};
+
+/*
+ * uni_reader_irq_handler
+ * In case of error audio stream is stopped; stop action is protected via PCM
+ * stream lock to avoid race condition with trigger callback.
+ */
+static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
+{
+ irqreturn_t ret = IRQ_NONE;
+ struct uniperif *reader = dev_id;
+ unsigned int status;
+
+ if (reader->state == UNIPERIF_STATE_STOPPED) {
+ /* Unexpected IRQ: do nothing */
+ dev_warn(reader->dev, "unexpected IRQ ");
+ return IRQ_HANDLED;
+ }
+
+ /* Get interrupt status & clear them immediately */
+ status = GET_UNIPERIF_ITS(reader);
+ SET_UNIPERIF_ITS_BCLR(reader, status);
+
+ /* Check for fifo overflow error */
+ if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) {
+ dev_err(reader->dev, "FIFO error detected");
+
+ snd_pcm_stream_lock(reader->substream);
+ snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock(reader->substream);
+
+ return IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int uni_reader_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *reader = priv->dai_data.uni;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int transfer_size, trigger_limit;
+ int slot_width;
+ int count = 10;
+
+ /* The reader should be stopped */
+ if (reader->state != UNIPERIF_STATE_STOPPED) {
+ dev_err(reader->dev, "%s: invalid reader state %d", __func__,
+ reader->state);
+ return -EINVAL;
+ }
+
+ /* Calculate transfer size (in fifo cells and bytes) for frame count */
+ transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
+
+ /* Calculate number of empty cells available before asserting DREQ */
+ if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
+ else
+ /*
+ * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
+ * FDMA_TRIGGER_LIMIT also controls when the state switches
+ * from OFF or STANDBY to AUDIO DATA.
+ */
+ trigger_limit = transfer_size;
+
+ /* Trigger limit must be an even number */
+ if ((!trigger_limit % 2) ||
+ (trigger_limit != 1 && transfer_size % 2) ||
+ (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
+ dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
+
+ switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_NB_IF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
+ break;
+ default:
+ SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
+ }
+
+ /* Force slot width to 32 in I2S mode */
+ if ((reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_I2S) {
+ slot_width = 32;
+ } else {
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ slot_width = 16;
+ break;
+ default:
+ slot_width = 32;
+ break;
+ }
+ }
+
+ /* Number of bits per subframe (i.e one channel sample) on input. */
+ switch (slot_width) {
+ case 32:
+ SET_UNIPERIF_I2S_FMT_NBIT_32(reader);
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(reader);
+ break;
+ case 16:
+ SET_UNIPERIF_I2S_FMT_NBIT_16(reader);
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(reader);
+ break;
+ default:
+ dev_err(reader->dev, "subframe format not supported");
+ return -EINVAL;
+ }
+
+ /* Configure data memory format */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /* One data word contains two samples */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_16(reader);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /*
+ * Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits
+ * on the MSB then zeros (if less than 32 bytes)"...
+ */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_0(reader);
+ break;
+
+ default:
+ dev_err(reader->dev, "format not supported");
+ return -EINVAL;
+ }
+
+ switch (reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
+ SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(reader);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
+ SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(reader);
+ SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader);
+ break;
+ default:
+ dev_err(reader->dev, "format not supported");
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
+
+ /* Data clocking (changing) on the rising edge */
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
+
+ /* Number of channels must be even */
+
+ if ((runtime->channels % 2) || (runtime->channels < 2) ||
+ (runtime->channels > 10)) {
+ dev_err(reader->dev, "%s: invalid nb of channels", __func__);
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
+
+ /* Clear any pending interrupts */
+ SET_UNIPERIF_ITS_BCLR(reader, GET_UNIPERIF_ITS(reader));
+
+ SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(reader, 0);
+
+ /* Set the interrupt mask */
+ SET_UNIPERIF_ITM_BSET_DMA_ERROR(reader);
+ SET_UNIPERIF_ITM_BSET_FIFO_ERROR(reader);
+ SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(reader);
+
+ /* Enable underflow recovery interrupts */
+ if (reader->info->underflow_enabled) {
+ SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(reader);
+ SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(reader);
+ }
+
+ /* Reset uniperipheral reader */
+ SET_UNIPERIF_SOFT_RST_SOFT_RST(reader);
+
+ while (GET_UNIPERIF_SOFT_RST_SOFT_RST(reader)) {
+ udelay(5);
+ count--;
+ }
+ if (!count) {
+ dev_err(reader->dev, "Failed to reset uniperif");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int uni_reader_start(struct uniperif *reader)
+{
+ /* The reader should be stopped */
+ if (reader->state != UNIPERIF_STATE_STOPPED) {
+ dev_err(reader->dev, "%s: invalid reader state", __func__);
+ return -EINVAL;
+ }
+
+ /* Enable reader interrupts (and clear possible stalled ones) */
+ SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(reader);
+ SET_UNIPERIF_ITM_BSET_FIFO_ERROR(reader);
+
+ /* Launch the reader */
+ SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(reader);
+
+ /* Update state to started */
+ reader->state = UNIPERIF_STATE_STARTED;
+ return 0;
+}
+
+static int uni_reader_stop(struct uniperif *reader)
+{
+ /* The reader should not be in stopped state */
+ if (reader->state == UNIPERIF_STATE_STOPPED) {
+ dev_err(reader->dev, "%s: invalid reader state", __func__);
+ return -EINVAL;
+ }
+
+ /* Turn the reader off */
+ SET_UNIPERIF_CTRL_OPERATION_OFF(reader);
+
+ /* Disable interrupts */
+ SET_UNIPERIF_ITM_BCLR(reader, GET_UNIPERIF_ITM(reader));
+
+ /* Update state to stopped and return */
+ reader->state = UNIPERIF_STATE_STOPPED;
+
+ return 0;
+}
+
+static int uni_reader_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *reader = priv->dai_data.uni;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ return uni_reader_start(reader);
+ case SNDRV_PCM_TRIGGER_STOP:
+ return uni_reader_stop(reader);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void uni_reader_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *reader = priv->dai_data.uni;
+
+ if (reader->state != UNIPERIF_STATE_STOPPED) {
+ /* Stop the reader */
+ uni_reader_stop(reader);
+ }
+}
+
+static int uni_reader_parse_dt(struct platform_device *pdev,
+ struct uniperif *reader)
+{
+ struct uniperif_info *info;
+ struct device_node *node = pdev->dev.of_node;
+
+ /* Allocate memory for the info structure */
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (of_property_read_u32(node, "st,version", &reader->ver) ||
+ reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
+ dev_err(&pdev->dev, "Unknown uniperipheral version ");
+ return -EINVAL;
+ }
+
+ /* Save the info structure */
+ reader->info = info;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops uni_reader_dai_ops = {
+ .shutdown = uni_reader_shutdown,
+ .prepare = uni_reader_prepare,
+ .trigger = uni_reader_trigger,
+ .hw_params = sti_uniperiph_dai_hw_params,
+ .set_fmt = sti_uniperiph_dai_set_fmt,
+};
+
+int uni_reader_init(struct platform_device *pdev,
+ struct uniperif *reader)
+{
+ int ret = 0;
+
+ reader->dev = &pdev->dev;
+ reader->state = UNIPERIF_STATE_STOPPED;
+ reader->hw = &uni_reader_pcm_hw;
+ reader->dai_ops = &uni_reader_dai_ops;
+
+ ret = uni_reader_parse_dt(pdev, reader);
+ if (ret < 0) {
+ dev_err(reader->dev, "Failed to parse DeviceTree");
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, reader->irq,
+ uni_reader_irq_handler, IRQF_SHARED,
+ dev_name(&pdev->dev), reader);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request IRQ");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uni_reader_init);
diff --git a/kernel/sound/soc/sunxi/Kconfig b/kernel/sound/soc/sunxi/Kconfig
new file mode 100644
index 000000000..84c72ec6a
--- /dev/null
+++ b/kernel/sound/soc/sunxi/Kconfig
@@ -0,0 +1,11 @@
+menu "Allwinner SoC Audio support"
+
+config SND_SUN4I_CODEC
+ tristate "Allwinner A10 Codec Support"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Select Y or M to add support for the Codec embedded in the Allwinner
+ A10 and affiliated SoCs.
+
+endmenu
diff --git a/kernel/sound/soc/sunxi/Makefile b/kernel/sound/soc/sunxi/Makefile
new file mode 100644
index 000000000..ea8a08c88
--- /dev/null
+++ b/kernel/sound/soc/sunxi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+
diff --git a/kernel/sound/soc/sunxi/sun4i-codec.c b/kernel/sound/soc/sunxi/sun4i-codec.c
new file mode 100644
index 000000000..1bb896d78
--- /dev/null
+++ b/kernel/sound/soc/sunxi/sun4i-codec.c
@@ -0,0 +1,713 @@
+/*
+ * Copyright 2014 Emilio López <emilio@elopez.com.ar>
+ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
+ * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ * Copyright 2015 Adam Sampson <ats@offog.org>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Codec DAC register offsets and bit fields */
+#define SUN4I_CODEC_DAC_DPC (0x00)
+#define SUN4I_CODEC_DAC_DPC_EN_DA (31)
+#define SUN4I_CODEC_DAC_DPC_DVOL (12)
+#define SUN4I_CODEC_DAC_FIFOC (0x04)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)
+#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)
+#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)
+#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)
+#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)
+#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)
+#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)
+#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)
+#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
+#define SUN4I_CODEC_DAC_FIFOS (0x08)
+#define SUN4I_CODEC_DAC_TXDATA (0x0c)
+#define SUN4I_CODEC_DAC_ACTL (0x10)
+#define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
+#define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
+#define SUN4I_CODEC_DAC_ACTL_MIXEN (29)
+#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)
+#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)
+#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)
+#define SUN4I_CODEC_DAC_ACTL_DACPAS (8)
+#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)
+#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)
+#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)
+#define SUN4I_CODEC_DAC_TUNE (0x14)
+#define SUN4I_CODEC_DAC_DEBUG (0x18)
+
+/* Codec ADC register offsets and bit fields */
+#define SUN4I_CODEC_ADC_FIFOC (0x1c)
+#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
+#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
+#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
+#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)
+#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)
+#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)
+#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
+#define SUN4I_CODEC_ADC_FIFOS (0x20)
+#define SUN4I_CODEC_ADC_RXDATA (0x24)
+#define SUN4I_CODEC_ADC_ACTL (0x28)
+#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
+#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
+#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)
+#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)
+#define SUN4I_CODEC_ADC_ACTL_VMICEN (27)
+#define SUN4I_CODEC_ADC_ACTL_VADCG (20)
+#define SUN4I_CODEC_ADC_ACTL_ADCIS (17)
+#define SUN4I_CODEC_ADC_ACTL_PA_EN (4)
+#define SUN4I_CODEC_ADC_ACTL_DDE (3)
+#define SUN4I_CODEC_ADC_DEBUG (0x2c)
+
+/* Other various ADC registers */
+#define SUN4I_CODEC_DAC_TXCNT (0x30)
+#define SUN4I_CODEC_ADC_RXCNT (0x34)
+#define SUN4I_CODEC_AC_SYS_VERI (0x38)
+#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c)
+
+struct sun4i_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *clk_apb;
+ struct clk *clk_module;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+};
+
+static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
+{
+ /*
+ * FIXME: according to the BSP, we might need to drive a PA
+ * GPIO high here on some boards
+ */
+
+ /* Flush TX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+ /* Enable DAC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
+}
+
+static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
+{
+ /*
+ * FIXME: according to the BSP, we might need to drive a PA
+ * GPIO low here on some boards
+ */
+
+ /* Disable DAC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+ 0);
+}
+
+static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sun4i_codec_start_playback(scodec);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sun4i_codec_stop_playback(scodec);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+ u32 val;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ /* Flush the TX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+ /* Set TX FIFO Empty Trigger Level */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
+ 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
+
+ if (substream->runtime->rate > 32000)
+ /* Use 64 bits FIR filter */
+ val = 0;
+ else
+ /* Use 32 bits FIR filter */
+ val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
+
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
+ val);
+
+ /* Send zeros when we have an underrun */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
+ 0);
+
+ return 0;
+}
+
+static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+
+ switch (rate) {
+ case 176400:
+ case 88200:
+ case 44100:
+ case 33075:
+ case 22050:
+ case 14700:
+ case 11025:
+ case 7350:
+ return 22579200;
+
+ case 192000:
+ case 96000:
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 12000:
+ case 8000:
+ return 24576000;
+
+ default:
+ return 0;
+ }
+}
+
+static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+
+ switch (rate) {
+ case 192000:
+ case 176400:
+ return 6;
+
+ case 96000:
+ case 88200:
+ return 7;
+
+ case 48000:
+ case 44100:
+ return 0;
+
+ case 32000:
+ case 33075:
+ return 1;
+
+ case 24000:
+ case 22050:
+ return 2;
+
+ case 16000:
+ case 14700:
+ return 3;
+
+ case 12000:
+ case 11025:
+ return 4;
+
+ case 8000:
+ case 7350:
+ return 5;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sun4i_codec_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 sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long clk_freq;
+ int ret, hwrate;
+ u32 val;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ clk_freq = sun4i_codec_get_mod_freq(params);
+ if (!clk_freq)
+ return -EINVAL;
+
+ ret = clk_set_rate(scodec->clk_module, clk_freq);
+ if (ret)
+ return ret;
+
+ hwrate = sun4i_codec_get_hw_rate(params);
+ if (hwrate < 0)
+ return hwrate;
+
+ /* Set DAC sample rate */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
+ hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
+
+ /* Set the number of channels we want to use */
+ if (params_channels(params) == 1)
+ val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
+ else
+ val = 0;
+
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
+ val);
+
+ /* Set the number of sample bits to either 16 or 24 bits */
+ if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
+
+ /* Set TX FIFO mode to padding the LSBs with 0 */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+ 0);
+
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ } else {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+ 0);
+
+ /* Set TX FIFO mode to repeat the MSB */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
+
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ }
+
+ return 0;
+}
+
+static int sun4i_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ /*
+ * Stop issuing DRQ when we have room for less than 16 samples
+ * in our TX FIFO
+ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
+
+ return clk_prepare_enable(scodec->clk_module);
+}
+
+static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(scodec->clk_module);
+}
+
+static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
+ .startup = sun4i_codec_startup,
+ .shutdown = sun4i_codec_shutdown,
+ .trigger = sun4i_codec_trigger,
+ .hw_params = sun4i_codec_hw_params,
+ .prepare = sun4i_codec_prepare,
+};
+
+static struct snd_soc_dai_driver sun4i_codec_dai = {
+ .name = "Codec",
+ .ops = &sun4i_codec_dai_ops,
+ .playback = {
+ .stream_name = "Codec Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
+ },
+};
+
+/*** Codec ***/
+static const struct snd_kcontrol_new sun4i_codec_pa_mute =
+ SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
+
+static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
+ SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
+ sun4i_codec_pa_volume_scale),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
+ SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
+ /* Digital parts of the DACs */
+ SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+ NULL, 0),
+
+ /* Analog parts of the DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ sun4i_codec_left_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ sun4i_codec_right_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_right_mixer_controls)),
+
+ /* Global Mixer Enable */
+ SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
+
+ /* Power Amplifier */
+ SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
+ sun4i_codec_pa_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
+ SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
+ &sun4i_codec_pa_mute),
+
+ SND_SOC_DAPM_OUTPUT("HP Right"),
+ SND_SOC_DAPM_OUTPUT("HP Left"),
+};
+
+static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
+ /* Left DAC Routes */
+ { "Left DAC", NULL, "DAC" },
+
+ /* Right DAC Routes */
+ { "Right DAC", NULL, "DAC" },
+
+ /* Right Mixer Routes */
+ { "Right Mixer", NULL, "Mixer Enable" },
+ { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
+ { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
+
+ /* Left Mixer Routes */
+ { "Left Mixer", NULL, "Mixer Enable" },
+ { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
+
+ /* Power Amplifier Routes */
+ { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
+ { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
+ { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
+ { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
+
+ /* Headphone Output Routes */
+ { "Power Amplifier Mute", "Switch", "Power Amplifier" },
+ { "HP Right", NULL, "Power Amplifier Mute" },
+ { "HP Left", NULL, "Power Amplifier Mute" },
+};
+
+static struct snd_soc_codec_driver sun4i_codec_codec = {
+ .controls = sun4i_codec_widgets,
+ .num_controls = ARRAY_SIZE(sun4i_codec_widgets),
+ .dapm_widgets = sun4i_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets),
+ .dapm_routes = sun4i_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes),
+};
+
+static const struct snd_soc_component_driver sun4i_codec_component = {
+ .name = "sun4i-codec",
+};
+
+#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000
+#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+ snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
+ NULL);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver dummy_cpu_dai = {
+ .name = "sun4i-codec-cpu-dai",
+ .probe = sun4i_codec_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SUN4I_CODEC_RATES,
+ .formats = SUN4I_CODEC_FORMATS,
+ .sig_bits = 24,
+ },
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-codec" },
+ { .compatible = "allwinner,sun7i-a20-codec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
+static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
+ int *num_links)
+{
+ struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
+ GFP_KERNEL);
+ if (!link)
+ return NULL;
+
+ link->name = "cdc";
+ link->stream_name = "CDC PCM";
+ link->codec_dai_name = "Codec";
+ link->cpu_dai_name = dev_name(dev);
+ link->codec_name = dev_name(dev);
+ link->platform_name = dev_name(dev);
+ link->dai_fmt = SND_SOC_DAIFMT_I2S;
+
+ *num_links = 1;
+
+ return link;
+};
+
+static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
+{
+ struct snd_soc_card *card;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return NULL;
+
+ card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+ if (!card->dai_link)
+ return NULL;
+
+ card->dev = dev;
+ card->name = "sun4i-codec";
+
+ return card;
+};
+
+static int sun4i_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sun4i_codec *scodec;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
+ if (!scodec)
+ return -ENOMEM;
+
+ scodec->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "Failed to map the registers\n");
+ return PTR_ERR(base);
+ }
+
+ scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sun4i_codec_regmap_config);
+ if (IS_ERR(scodec->regmap)) {
+ dev_err(&pdev->dev, "Failed to create our regmap\n");
+ return PTR_ERR(scodec->regmap);
+ }
+
+ /* Get the clocks from the DT */
+ scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
+ if (IS_ERR(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to get the APB clock\n");
+ return PTR_ERR(scodec->clk_apb);
+ }
+
+ scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
+ if (IS_ERR(scodec->clk_module)) {
+ dev_err(&pdev->dev, "Failed to get the module clock\n");
+ return PTR_ERR(scodec->clk_module);
+ }
+
+ /* Enable the bus clock */
+ if (clk_prepare_enable(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to enable the APB clock\n");
+ return -EINVAL;
+ }
+
+ /* DMA configuration for TX FIFO */
+ scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
+ scodec->playback_dma_data.maxburst = 4;
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+ &sun4i_codec_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our codec\n");
+ goto err_clk_disable;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sun4i_codec_component,
+ &dummy_cpu_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our DAI\n");
+ goto err_unregister_codec;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
+ goto err_unregister_codec;
+ }
+
+ card = sun4i_codec_create_card(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "Failed to create our card\n");
+ goto err_unregister_codec;
+ }
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, scodec);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our card\n");
+ goto err_unregister_codec;
+ }
+
+ return 0;
+
+err_unregister_codec:
+ snd_soc_unregister_codec(&pdev->dev);
+err_clk_disable:
+ clk_disable_unprepare(scodec->clk_apb);
+ return ret;
+}
+
+static int sun4i_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+ snd_soc_unregister_codec(&pdev->dev);
+ clk_disable_unprepare(scodec->clk_apb);
+
+ return 0;
+}
+
+static struct platform_driver sun4i_codec_driver = {
+ .driver = {
+ .name = "sun4i-codec",
+ .of_match_table = sun4i_codec_of_match,
+ },
+ .probe = sun4i_codec_probe,
+ .remove = sun4i_codec_remove,
+};
+module_platform_driver(sun4i_codec_driver);
+
+MODULE_DESCRIPTION("Allwinner A10 codec driver");
+MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/tegra/tegra20_das.c b/kernel/sound/soc/tegra/tegra20_das.c
index f52600b4f..89add13c3 100644
--- a/kernel/sound/soc/tegra/tegra20_das.c
+++ b/kernel/sound/soc/tegra/tegra20_das.c
@@ -133,7 +133,7 @@ static const struct regmap_config tegra20_das_regmap_config = {
static int tegra20_das_probe(struct platform_device *pdev)
{
- struct resource *res, *region;
+ struct resource *res;
void __iomem *regs;
int ret = 0;
@@ -149,24 +149,9 @@ static int tegra20_das_probe(struct platform_device *pdev)
das->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err;
- }
-
- region = devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err;
- }
-
- regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
goto err;
}
diff --git a/kernel/sound/soc/tegra/tegra20_i2s.c b/kernel/sound/soc/tegra/tegra20_i2s.c
index 05f1c6ee9..14106fa82 100644
--- a/kernel/sound/soc/tegra/tegra20_i2s.c
+++ b/kernel/sound/soc/tegra/tegra20_i2s.c
@@ -339,7 +339,7 @@ static const struct regmap_config tegra20_i2s_regmap_config = {
static int tegra20_i2s_platform_probe(struct platform_device *pdev)
{
struct tegra20_i2s *i2s;
- struct resource *mem, *memregion;
+ struct resource *mem;
void __iomem *regs;
int ret;
@@ -362,24 +362,9 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
- memregion = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem), DRV_NAME);
- if (!memregion) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err_clk_put;
- }
-
- regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
+ regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
goto err_clk_put;
}
diff --git a/kernel/sound/soc/tegra/tegra20_spdif.c b/kernel/sound/soc/tegra/tegra20_spdif.c
index 9141477a5..a0c364057 100644
--- a/kernel/sound/soc/tegra/tegra20_spdif.c
+++ b/kernel/sound/soc/tegra/tegra20_spdif.c
@@ -265,7 +265,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = {
static int tegra20_spdif_platform_probe(struct platform_device *pdev)
{
struct tegra20_spdif *spdif;
- struct resource *mem, *memregion, *dmareq;
+ struct resource *mem, *dmareq;
void __iomem *regs;
int ret;
@@ -273,45 +273,26 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!spdif) {
dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
dev_set_drvdata(&pdev->dev, spdif);
- spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
+ spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
if (IS_ERR(spdif->clk_spdif_out)) {
pr_err("Can't retrieve spdif clock\n");
ret = PTR_ERR(spdif->clk_spdif_out);
- goto err;
+ return ret;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
+ regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmareq) {
dev_err(&pdev->dev, "No DMA resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
- memregion = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem), DRV_NAME);
- if (!memregion) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err_clk_put;
- }
-
- regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto err_clk_put;
+ return -ENODEV;
}
spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
@@ -319,7 +300,7 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
if (IS_ERR(spdif->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
ret = PTR_ERR(spdif->regmap);
- goto err_clk_put;
+ return ret;
}
spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
@@ -335,7 +316,7 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
}
ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
- &tegra20_spdif_dai, 1);
+ &tegra20_spdif_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
@@ -357,16 +338,12 @@ err_suspend:
tegra20_spdif_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
-err_clk_put:
- clk_put(spdif->clk_spdif_out);
-err:
+
return ret;
}
static int tegra20_spdif_platform_remove(struct platform_device *pdev)
{
- struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
-
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_spdif_runtime_suspend(&pdev->dev);
@@ -374,8 +351,6 @@ static int tegra20_spdif_platform_remove(struct platform_device *pdev)
tegra_pcm_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
- clk_put(spdif->clk_spdif_out);
-
return 0;
}
diff --git a/kernel/sound/soc/tegra/tegra30_ahub.c b/kernel/sound/soc/tegra/tegra30_ahub.c
index bc94e5d8e..fef3b9a21 100644
--- a/kernel/sound/soc/tegra/tegra30_ahub.c
+++ b/kernel/sound/soc/tegra/tegra30_ahub.c
@@ -521,7 +521,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
const struct tegra30_ahub_soc_data *soc_data;
struct reset_control *rst;
int i;
- struct resource *res0, *res1, *region;
+ struct resource *res0, *res1;
void __iomem *regs_apbif, *regs_ahub;
int ret = 0;
@@ -549,103 +549,67 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Can't get reset %s\n",
configlink_mods[i].rst_name);
ret = PTR_ERR(rst);
- goto err;
+ return ret;
}
ret = reset_control_deassert(rst);
reset_control_put(rst);
if (ret)
- goto err;
+ return ret;
}
ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub),
GFP_KERNEL);
if (!ahub) {
dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
dev_set_drvdata(&pdev->dev, ahub);
ahub->soc_data = soc_data;
ahub->dev = &pdev->dev;
- ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio");
+ ahub->clk_d_audio = devm_clk_get(&pdev->dev, "d_audio");
if (IS_ERR(ahub->clk_d_audio)) {
dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n");
ret = PTR_ERR(ahub->clk_d_audio);
- goto err;
+ return ret;
}
- ahub->clk_apbif = clk_get(&pdev->dev, "apbif");
+ ahub->clk_apbif = devm_clk_get(&pdev->dev, "apbif");
if (IS_ERR(ahub->clk_apbif)) {
dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n");
ret = PTR_ERR(ahub->clk_apbif);
- goto err_clk_put_d_audio;
+ return ret;
}
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res0) {
- dev_err(&pdev->dev, "No apbif memory resource\n");
- ret = -ENODEV;
- goto err_clk_put_apbif;
- }
+ regs_apbif = devm_ioremap_resource(&pdev->dev, res0);
+ if (IS_ERR(regs_apbif))
+ return PTR_ERR(regs_apbif);
- region = devm_request_mem_region(&pdev->dev, res0->start,
- resource_size(res0), DRV_NAME);
- if (!region) {
- dev_err(&pdev->dev, "request region apbif failed\n");
- ret = -EBUSY;
- goto err_clk_put_apbif;
- }
ahub->apbif_addr = res0->start;
- regs_apbif = devm_ioremap(&pdev->dev, res0->start,
- resource_size(res0));
- if (!regs_apbif) {
- dev_err(&pdev->dev, "ioremap apbif failed\n");
- ret = -ENOMEM;
- goto err_clk_put_apbif;
- }
-
ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif,
&tegra30_ahub_apbif_regmap_config);
if (IS_ERR(ahub->regmap_apbif)) {
dev_err(&pdev->dev, "apbif regmap init failed\n");
ret = PTR_ERR(ahub->regmap_apbif);
- goto err_clk_put_apbif;
+ return ret;
}
regcache_cache_only(ahub->regmap_apbif, true);
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res1) {
- dev_err(&pdev->dev, "No ahub memory resource\n");
- ret = -ENODEV;
- goto err_clk_put_apbif;
- }
-
- region = devm_request_mem_region(&pdev->dev, res1->start,
- resource_size(res1), DRV_NAME);
- if (!region) {
- dev_err(&pdev->dev, "request region ahub failed\n");
- ret = -EBUSY;
- goto err_clk_put_apbif;
- }
-
- regs_ahub = devm_ioremap(&pdev->dev, res1->start,
- resource_size(res1));
- if (!regs_ahub) {
- dev_err(&pdev->dev, "ioremap ahub failed\n");
- ret = -ENOMEM;
- goto err_clk_put_apbif;
- }
+ regs_ahub = devm_ioremap_resource(&pdev->dev, res1);
+ if (IS_ERR(regs_ahub))
+ return PTR_ERR(regs_ahub);
ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub,
&tegra30_ahub_ahub_regmap_config);
if (IS_ERR(ahub->regmap_ahub)) {
dev_err(&pdev->dev, "ahub regmap init failed\n");
ret = PTR_ERR(ahub->regmap_ahub);
- goto err_clk_put_apbif;
+ return ret;
}
regcache_cache_only(ahub->regmap_ahub, true);
@@ -662,12 +626,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
err_pm_disable:
pm_runtime_disable(&pdev->dev);
-err_clk_put_apbif:
- clk_put(ahub->clk_apbif);
-err_clk_put_d_audio:
- clk_put(ahub->clk_d_audio);
- ahub = NULL;
-err:
+
return ret;
}
@@ -680,11 +639,6 @@ static int tegra30_ahub_remove(struct platform_device *pdev)
if (!pm_runtime_status_suspended(&pdev->dev))
tegra30_ahub_runtime_suspend(&pdev->dev);
- clk_put(ahub->clk_apbif);
- clk_put(ahub->clk_d_audio);
-
- ahub = NULL;
-
return 0;
}
diff --git a/kernel/sound/soc/tegra/tegra30_i2s.c b/kernel/sound/soc/tegra/tegra30_i2s.c
index fe36375ba..8e55583aa 100644
--- a/kernel/sound/soc/tegra/tegra30_i2s.c
+++ b/kernel/sound/soc/tegra/tegra30_i2s.c
@@ -379,7 +379,7 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
struct tegra30_i2s *i2s;
const struct of_device_id *match;
u32 cif_ids[2];
- struct resource *mem, *memregion;
+ struct resource *mem;
void __iomem *regs;
int ret;
@@ -419,24 +419,9 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
- memregion = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem), DRV_NAME);
- if (!memregion) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err_clk_put;
- }
-
- regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
+ regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
goto err_clk_put;
}
diff --git a/kernel/sound/soc/txx9/txx9aclc.c b/kernel/sound/soc/txx9/txx9aclc.c
index 88eacfd83..a8f705bb6 100644
--- a/kernel/sound/soc/txx9/txx9aclc.c
+++ b/kernel/sound/soc/txx9/txx9aclc.c
@@ -411,13 +411,8 @@ static struct snd_soc_platform_driver txx9aclc_soc_platform = {
static int txx9aclc_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &txx9aclc_soc_platform);
-}
-
-static int txx9aclc_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_platform(&pdev->dev,
+ &txx9aclc_soc_platform);
}
static struct platform_driver txx9aclc_pcm_driver = {
@@ -426,7 +421,6 @@ static struct platform_driver txx9aclc_pcm_driver = {
},
.probe = txx9aclc_soc_platform_probe,
- .remove = txx9aclc_soc_platform_remove,
};
module_platform_driver(txx9aclc_pcm_driver);
diff --git a/kernel/sound/soc/ux500/mop500.c b/kernel/sound/soc/ux500/mop500.c
index 4e0c0e502..ba9fc099c 100644
--- a/kernel/sound/soc/ux500/mop500.c
+++ b/kernel/sound/soc/ux500/mop500.c
@@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = {
{ .compatible = "stericsson,snd-soc-mop500", },
{},
};
+MODULE_DEVICE_TABLE(of, snd_soc_mop500_match);
static struct platform_driver snd_soc_mop500_driver = {
.driver = {
diff --git a/kernel/sound/soc/ux500/mop500_ab8500.c b/kernel/sound/soc/ux500/mop500_ab8500.c
index b81a7a4c9..85d810d76 100644
--- a/kernel/sound/soc/ux500/mop500_ab8500.c
+++ b/kernel/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/kernel/sound/soc/ux500/ux500_msp_dai.c b/kernel/sound/soc/ux500/ux500_msp_dai.c
index 978f2d731..6d5698b25 100644
--- a/kernel/sound/soc/ux500/ux500_msp_dai.c
+++ b/kernel/sound/soc/ux500/ux500_msp_dai.c
@@ -522,9 +522,9 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
slots_active = hweight32(mask);
dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- slots_active, slots_active);
+ slots_active);
break;
default:
@@ -773,20 +773,22 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
}
prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
- drvdata->pclk = clk_get(&pdev->dev, "apb_pclk");
+ drvdata->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(drvdata->pclk)) {
ret = (int)PTR_ERR(drvdata->pclk);
- dev_err(&pdev->dev, "%s: ERROR: clk_get of pclk failed (%d)!\n",
+ dev_err(&pdev->dev,
+ "%s: ERROR: devm_clk_get of pclk failed (%d)!\n",
__func__, ret);
- goto err_pclk;
+ return ret;
}
- drvdata->clk = clk_get(&pdev->dev, NULL);
+ drvdata->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(drvdata->clk)) {
ret = (int)PTR_ERR(drvdata->clk);
- dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n",
+ dev_err(&pdev->dev,
+ "%s: ERROR: devm_clk_get failed (%d)!\n",
__func__, ret);
- goto err_clk;
+ return ret;
}
ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp,
@@ -795,7 +797,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"%s: ERROR: Failed to init MSP-struct (%d)!",
__func__, ret);
- goto err_init_msp;
+ return ret;
}
dev_set_drvdata(&pdev->dev, drvdata);
@@ -804,7 +806,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
__func__, drvdata->msp->id);
- goto err_init_msp;
+ return ret;
}
ret = ux500_pcm_register_platform(pdev);
@@ -819,13 +821,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
err_reg_plat:
snd_soc_unregister_component(&pdev->dev);
-err_init_msp:
- clk_put(drvdata->clk);
-err_clk:
- clk_put(drvdata->pclk);
-err_pclk:
- devm_regulator_put(drvdata->reg_vape);
-
return ret;
}
@@ -837,12 +832,8 @@ static int ux500_msp_drv_remove(struct platform_device *pdev)
snd_soc_unregister_component(&pdev->dev);
- devm_regulator_put(drvdata->reg_vape);
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
- clk_put(drvdata->clk);
- clk_put(drvdata->pclk);
-
ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
return 0;
@@ -852,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = {
{ .compatible = "stericsson,ux500-msp-i2s", },
{},
};
+MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match);
static struct platform_driver msp_i2s_driver = {
.driver = {
diff --git a/kernel/sound/soc/ux500/ux500_pcm.c b/kernel/sound/soc/ux500/ux500_pcm.c
index 51a66a873..f12c01ddd 100644
--- a/kernel/sound/soc/ux500/ux500_pcm.c
+++ b/kernel/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/kernel/sound/soc/xtensa/xtfpga-i2s.c b/kernel/sound/soc/xtensa/xtfpga-i2s.c
index 1cfb19e12..8382ffa3b 100644
--- a/kernel/sound/soc/xtensa/xtfpga-i2s.c
+++ b/kernel/sound/soc/xtensa/xtfpga-i2s.c
@@ -75,7 +75,7 @@ struct xtfpga_i2s {
* stream in the pcm_close callback it synchronizes with the interrupt
* handler by means of synchronize_rcu call.
*/
- struct snd_pcm_substream *tx_substream;
+ struct snd_pcm_substream __rcu *tx_substream;
unsigned (*tx_fn)(struct xtfpga_i2s *i2s,
struct snd_pcm_runtime *runtime,
unsigned tx_ptr);
@@ -474,11 +474,6 @@ static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd)
card->dev, size, size);
}
-static void xtfpga_pcm_free(struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
static const struct snd_pcm_ops xtfpga_pcm_ops = {
.open = xtfpga_pcm_open,
.close = xtfpga_pcm_close,
@@ -490,7 +485,6 @@ static const struct snd_pcm_ops xtfpga_pcm_ops = {
static const struct snd_soc_platform_driver xtfpga_soc_platform = {
.pcm_new = xtfpga_pcm_new,
- .pcm_free = xtfpga_pcm_free,
.ops = &xtfpga_pcm_ops,
};
diff --git a/kernel/sound/soc/zte/Kconfig b/kernel/sound/soc/zte/Kconfig
new file mode 100644
index 000000000..c47eb25e4
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/zte/Makefile b/kernel/sound/soc/zte/Makefile
new file mode 100644
index 000000000..254ed2c8c
--- /dev/null
+++ b/kernel/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/kernel/sound/soc/zte/zx296702-i2s.c b/kernel/sound/soc/zte/zx296702-i2s.c
new file mode 100644
index 000000000..1cad93dc1
--- /dev/null
+++ b/kernel/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 = devm_kzalloc(&pdev->dev, 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 (IS_ERR(zx_i2s->reg_base)) {
+ dev_err(&pdev->dev, "ioremap failed!\n");
+ return PTR_ERR(zx_i2s->reg_base);
+ }
+
+ writel_relaxed(0, zx_i2s->reg_base + ZX_I2S_FIFO_CTRL);
+ platform_set_drvdata(pdev, zx_i2s);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &zx_i2s_component,
+ &zx_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+ return ret;
+ }
+
+ 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/kernel/sound/soc/zte/zx296702-spdif.c b/kernel/sound/soc/zte/zx296702-spdif.c
new file mode 100644
index 000000000..26265ce4c
--- /dev/null
+++ b/kernel/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 (IS_ERR(zx_spdif->reg_base)) {
+ dev_err(&pdev->dev, "ioremap failed!\n");
+ return PTR_ERR(zx_spdif->reg_base);
+ }
+
+ 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/kernel/sound/sound_core.c b/kernel/sound/sound_core.c
index 11e953a1f..99b73c675 100644
--- a/kernel/sound/sound_core.c
+++ b/kernel/sound/sound_core.c
@@ -655,7 +655,7 @@ static void cleanup_oss_soundcore(void)
static int __init init_oss_soundcore(void)
{
if (preclaim_oss &&
- register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {
+ register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) < 0) {
printk(KERN_ERR "soundcore: sound device already in use.\n");
return -EBUSY;
}
diff --git a/kernel/sound/sound_firmware.c b/kernel/sound/sound_firmware.c
index b155137ee..026347643 100644
--- a/kernel/sound/sound_firmware.c
+++ b/kernel/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/kernel/sound/sparc/Kconfig b/kernel/sound/sparc/Kconfig
index d75deba56..dfcd38647 100644
--- a/kernel/sound/sparc/Kconfig
+++ b/kernel/sound/sparc/Kconfig
@@ -22,6 +22,7 @@ config SND_SUN_AMD7930
config SND_SUN_CS4231
tristate "Sun CS4231"
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for CS4231 sound device on Sun.
diff --git a/kernel/sound/sparc/amd7930.c b/kernel/sound/sparc/amd7930.c
index 1b1a89e80..35c1f6ae7 100644
--- a/kernel/sound/sparc/amd7930.c
+++ b/kernel/sound/sparc/amd7930.c
@@ -956,6 +956,7 @@ static int snd_amd7930_create(struct snd_card *card,
if (!amd->regs) {
snd_printk(KERN_ERR
"amd7930-%d: Unable to map chip registers.\n", dev);
+ kfree(amd);
return -EIO;
}
@@ -1063,6 +1064,7 @@ static const struct of_device_id amd7930_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, amd7930_match);
static struct platform_driver amd7930_sbus_driver = {
.driver = {
diff --git a/kernel/sound/synth/emux/Makefile b/kernel/sound/synth/emux/Makefile
index 328594e61..fb761c2c2 100644
--- a/kernel/sound/synth/emux/Makefile
+++ b/kernel/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/kernel/sound/synth/emux/emux.c b/kernel/sound/synth/emux/emux.c
index 49195325f..9312cd8a6 100644
--- a/kernel/sound/synth/emux/emux.c
+++ b/kernel/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/kernel/sound/synth/emux/emux_oss.c b/kernel/sound/synth/emux/emux_oss.c
index 82e350e95..ac75816ad 100644
--- a/kernel/sound/synth/emux/emux_oss.c
+++ b/kernel/sound/synth/emux/emux_oss.c
@@ -69,7 +69,8 @@ snd_emux_init_seq_oss(struct snd_emux *emu)
struct snd_seq_oss_reg *arg;
struct snd_seq_device *dev;
- if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS,
+ /* using device#1 here for avoiding conflicts with OPL3 */
+ if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS,
sizeof(struct snd_seq_oss_reg), &dev) < 0)
return;
diff --git a/kernel/sound/synth/emux/emux_proc.c b/kernel/sound/synth/emux/emux_proc.c
index 58a32a10d..a82b4053b 100644
--- a/kernel/sound/synth/emux/emux_proc.c
+++ b/kernel/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/kernel/sound/synth/emux/emux_voice.h b/kernel/sound/synth/emux/emux_voice.h
index 09711f84e..a7073c371 100644
--- a/kernel/sound/synth/emux/emux_voice.h
+++ b/kernel/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/kernel/sound/usb/bcd2000/bcd2000.c b/kernel/sound/usb/bcd2000/bcd2000.c
index 820d6ca8c..d060dddcc 100644
--- a/kernel/sound/usb/bcd2000/bcd2000.c
+++ b/kernel/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/kernel/sound/usb/card.c b/kernel/sound/usb/card.c
index 045059398..1f09d9591 100644
--- a/kernel/sound/usb/card.c
+++ b/kernel/sound/usb/card.c
@@ -365,13 +365,15 @@ static int snd_usb_audio_create(struct usb_interface *intf,
}
mutex_init(&chip->mutex);
- init_rwsem(&chip->shutdown_rwsem);
+ init_waitqueue_head(&chip->shutdown_wait);
chip->index = idx;
chip->dev = dev;
chip->card = card;
chip->setup = device_setup[idx];
chip->autoclock = autoclock;
- chip->probing = 1;
+ atomic_set(&chip->active, 1); /* avoid autopm during probing */
+ atomic_set(&chip->usage_count, 0);
+ atomic_set(&chip->shutdown, 0);
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
@@ -495,13 +497,13 @@ static int usb_audio_probe(struct usb_interface *intf,
mutex_lock(&register_mutex);
for (i = 0; i < SNDRV_CARDS; i++) {
if (usb_chip[i] && usb_chip[i]->dev == dev) {
- if (usb_chip[i]->shutdown) {
+ if (atomic_read(&usb_chip[i]->shutdown)) {
dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n");
err = -EIO;
goto __error;
}
chip = usb_chip[i];
- chip->probing = 1;
+ atomic_inc(&chip->active); /* avoid autopm */
break;
}
}
@@ -561,8 +563,8 @@ static int usb_audio_probe(struct usb_interface *intf,
usb_chip[chip->index] = chip;
chip->num_interfaces++;
- chip->probing = 0;
usb_set_intfdata(intf, chip);
+ atomic_dec(&chip->active);
mutex_unlock(&register_mutex);
return 0;
@@ -570,7 +572,7 @@ static int usb_audio_probe(struct usb_interface *intf,
if (chip) {
if (!chip->num_interfaces)
snd_card_free(chip->card);
- chip->probing = 0;
+ atomic_dec(&chip->active);
}
mutex_unlock(&register_mutex);
return err;
@@ -585,23 +587,23 @@ static void usb_audio_disconnect(struct usb_interface *intf)
struct snd_usb_audio *chip = usb_get_intfdata(intf);
struct snd_card *card;
struct list_head *p;
- bool was_shutdown;
if (chip == (void *)-1L)
return;
card = chip->card;
- down_write(&chip->shutdown_rwsem);
- was_shutdown = chip->shutdown;
- chip->shutdown = 1;
- up_write(&chip->shutdown_rwsem);
mutex_lock(&register_mutex);
- if (!was_shutdown) {
+ if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
struct snd_usb_endpoint *ep;
struct usb_mixer_interface *mixer;
+ /* wait until all pending tasks done;
+ * they are protected by snd_usb_lock_shutdown()
+ */
+ wait_event(chip->shutdown_wait,
+ !atomic_read(&chip->usage_count));
snd_card_disconnect(card);
/* release the pcm resources */
list_for_each_entry(as, &chip->pcm_list, list) {
@@ -631,28 +633,52 @@ static void usb_audio_disconnect(struct usb_interface *intf)
}
}
-#ifdef CONFIG_PM
-
-int snd_usb_autoresume(struct snd_usb_audio *chip)
+/* lock the shutdown (disconnect) task and autoresume */
+int snd_usb_lock_shutdown(struct snd_usb_audio *chip)
{
- int err = -ENODEV;
+ int err;
- down_read(&chip->shutdown_rwsem);
- if (chip->probing || chip->in_pm)
- err = 0;
- else if (!chip->shutdown)
- err = usb_autopm_get_interface(chip->pm_intf);
- up_read(&chip->shutdown_rwsem);
+ atomic_inc(&chip->usage_count);
+ if (atomic_read(&chip->shutdown)) {
+ err = -EIO;
+ goto error;
+ }
+ err = snd_usb_autoresume(chip);
+ if (err < 0)
+ goto error;
+ return 0;
+ error:
+ if (atomic_dec_and_test(&chip->usage_count))
+ wake_up(&chip->shutdown_wait);
return err;
}
+/* autosuspend and unlock the shutdown */
+void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
+{
+ snd_usb_autosuspend(chip);
+ if (atomic_dec_and_test(&chip->usage_count))
+ wake_up(&chip->shutdown_wait);
+}
+
+#ifdef CONFIG_PM
+
+int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+ if (atomic_read(&chip->shutdown))
+ return -EIO;
+ if (atomic_inc_return(&chip->active) == 1)
+ return usb_autopm_get_interface(chip->pm_intf);
+ return 0;
+}
+
void snd_usb_autosuspend(struct snd_usb_audio *chip)
{
- down_read(&chip->shutdown_rwsem);
- if (!chip->shutdown && !chip->probing && !chip->in_pm)
+ if (atomic_read(&chip->shutdown))
+ return;
+ if (atomic_dec_and_test(&chip->active))
usb_autopm_put_interface(chip->pm_intf);
- up_read(&chip->shutdown_rwsem);
}
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
@@ -665,30 +691,20 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
if (chip == (void *)-1L)
return 0;
- if (!PMSG_IS_AUTO(message)) {
+ chip->autosuspended = !!PMSG_IS_AUTO(message);
+ if (!chip->autosuspended)
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
- if (!chip->num_suspended_intf++) {
- list_for_each_entry(as, &chip->pcm_list, list) {
- snd_pcm_suspend_all(as->pcm);
- as->substream[0].need_setup_ep =
- as->substream[1].need_setup_ep = true;
- }
- list_for_each(p, &chip->midi_list) {
- snd_usbmidi_suspend(p);
- }
+ if (!chip->num_suspended_intf++) {
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ snd_pcm_suspend_all(as->pcm);
+ as->substream[0].need_setup_ep =
+ as->substream[1].need_setup_ep = true;
}
- } else {
- /*
- * otherwise we keep the rest of the system in the dark
- * to keep this transparent
- */
- if (!chip->num_suspended_intf++)
- chip->autosuspended = 1;
- }
-
- if (chip->num_suspended_intf == 1)
+ list_for_each(p, &chip->midi_list)
+ snd_usbmidi_suspend(p);
list_for_each_entry(mixer, &chip->mixer_list, list)
snd_usb_mixer_suspend(mixer);
+ }
return 0;
}
@@ -705,7 +721,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
if (--chip->num_suspended_intf)
return 0;
- chip->in_pm = 1;
+ atomic_inc(&chip->active); /* avoid autopm */
/*
* ALSA leaves material resumption to user space
* we just notify and restart the mixers
@@ -725,7 +741,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
chip->autosuspended = 0;
err_out:
- chip->in_pm = 0;
+ atomic_dec(&chip->active); /* allow autopm after this point */
return err;
}
diff --git a/kernel/sound/usb/card.h b/kernel/sound/usb/card.h
index ef580b43f..71778ca4b 100644
--- a/kernel/sound/usb/card.h
+++ b/kernel/sound/usb/card.h
@@ -122,6 +122,7 @@ struct snd_usb_substream {
unsigned int buffer_periods; /* current periods per buffer */
unsigned int altset_idx; /* USB data format: index of alternate setting */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
+ unsigned int tx_length_quirk:1; /* add length specifier to transfers */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
diff --git a/kernel/sound/usb/endpoint.c b/kernel/sound/usb/endpoint.c
index 03b074419..7b1cb365f 100644
--- a/kernel/sound/usb/endpoint.c
+++ b/kernel/sound/usb/endpoint.c
@@ -183,13 +183,53 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
ep->retire_data_urb(ep->data_subs, urb);
}
+static void prepare_silent_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ struct urb *urb = ctx->urb;
+ unsigned int offs = 0;
+ unsigned int extra = 0;
+ __le32 packet_length;
+ int i;
+
+ /* For tx_length_quirk, put packet length at start of packet */
+ if (ep->chip->tx_length_quirk)
+ extra = sizeof(packet_length);
+
+ for (i = 0; i < ctx->packets; ++i) {
+ unsigned int offset;
+ unsigned int length;
+ int counts;
+
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
+ length = counts * ep->stride; /* number of silent bytes */
+ offset = offs * ep->stride + extra * i;
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = length + extra;
+ if (extra) {
+ packet_length = cpu_to_le32(length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ }
+ memset(urb->transfer_buffer + offset + extra,
+ ep->silence_value, length);
+ offs += counts;
+ }
+
+ urb->number_of_packets = ctx->packets;
+ urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
+}
+
/*
* Prepare a PLAYBACK urb for submission to the bus.
*/
static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
- int i;
struct urb *urb = ctx->urb;
unsigned char *cp = urb->transfer_buffer;
@@ -201,24 +241,7 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
ep->prepare_data_urb(ep->data_subs, urb);
} else {
/* no data provider, so send silence */
- unsigned int offs = 0;
- for (i = 0; i < ctx->packets; ++i) {
- int counts;
-
- if (ctx->packet_size[i])
- counts = ctx->packet_size[i];
- else
- counts = snd_usb_endpoint_next_packet_size(ep);
-
- urb->iso_frame_desc[i].offset = offs * ep->stride;
- urb->iso_frame_desc[i].length = counts * ep->stride;
- offs += counts;
- }
-
- urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * ep->stride;
- memset(urb->transfer_buffer, ep->silence_value,
- offs * ep->stride);
+ prepare_silent_urb(ep, ctx);
}
break;
@@ -355,8 +378,10 @@ static void snd_complete_urb(struct urb *urb)
if (unlikely(urb->status == -ENOENT || /* unlinked */
urb->status == -ENODEV || /* device removed */
urb->status == -ECONNRESET || /* unlinked */
- urb->status == -ESHUTDOWN || /* device disabled */
- ep->chip->shutdown)) /* device disconnected */
+ urb->status == -ESHUTDOWN)) /* device disabled */
+ goto exit_clear;
+ /* device disconnected */
+ if (unlikely(atomic_read(&ep->chip->shutdown)))
goto exit_clear;
if (usb_pipeout(ep->pipe)) {
@@ -529,7 +554,7 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force)
{
unsigned int i;
- if (!force && ep->chip->shutdown) /* to be sure... */
+ if (!force && atomic_read(&ep->chip->shutdown)) /* to be sure... */
return -EBADFD;
clear_bit(EP_FLAG_RUNNING, &ep->flags);
@@ -592,6 +617,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
unsigned int max_packs_per_period, urbs_per_period, urb_packs;
unsigned int max_urbs, i;
int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+ int tx_length_quirk = (ep->chip->tx_length_quirk &&
+ usb_pipeout(ep->pipe));
if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
/*
@@ -608,13 +635,34 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
/* assume max. frequency is 25% higher than nominal */
ep->freqmax = ep->freqn + (ep->freqn >> 2);
- maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
- >> (16 - ep->datainterval);
+ /* Round up freqmax to nearest integer in order to calculate maximum
+ * packet size, which must represent a whole number of frames.
+ * This is accomplished by adding 0x0.ffff before converting the
+ * Q16.16 format into integer.
+ * In order to accurately calculate the maximum packet size when
+ * the data interval is more than 1 (i.e. ep->datainterval > 0),
+ * multiply by the data interval prior to rounding. For instance,
+ * a freqmax of 41 kHz will result in a max packet size of 6 (5.125)
+ * frames with a data interval of 1, but 11 (10.25) frames with a
+ * data interval of 2.
+ * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the
+ * maximum datainterval value of 3, at USB full speed, higher for
+ * USB high speed, noting that ep->freqmax is in units of
+ * frames per packet in Q16.16 format.)
+ */
+ maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) *
+ (frame_bits >> 3);
+ if (tx_length_quirk)
+ maxsize += sizeof(__le32); /* Space for length descriptor */
/* but wMaxPacketSize might reduce this */
if (ep->maxpacksize && ep->maxpacksize < maxsize) {
/* whatever fits into a max. size packet */
- maxsize = ep->maxpacksize;
- ep->freqmax = (maxsize / (frame_bits >> 3))
+ unsigned int data_maxsize = maxsize = ep->maxpacksize;
+
+ if (tx_length_quirk)
+ /* Need to remove the length descriptor to calc freq */
+ data_maxsize -= sizeof(__le32);
+ ep->freqmax = (data_maxsize / (frame_bits >> 3))
<< (16 - ep->datainterval);
}
@@ -868,7 +916,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
int err;
unsigned int i;
- if (ep->chip->shutdown)
+ if (atomic_read(&ep->chip->shutdown))
return -EBADFD;
/* already running? */
diff --git a/kernel/sound/usb/midi.c b/kernel/sound/usb/midi.c
index 417ebb11c..b21b76690 100644
--- a/kernel/sound/usb/midi.c
+++ b/kernel/sound/usb/midi.c
@@ -174,6 +174,8 @@ struct snd_usb_midi_in_endpoint {
u8 running_status_length;
} ports[0x10];
u8 seen_f5;
+ bool in_sysex;
+ u8 last_cin;
u8 error_resubmit;
int current_port;
};
@@ -468,6 +470,39 @@ static void snd_usbmidi_maudio_broken_running_status_input(
}
/*
+ * QinHeng CH345 is buggy: every second packet inside a SysEx has not CIN 4
+ * but the previously seen CIN, but still with three data bytes.
+ */
+static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep,
+ uint8_t *buffer, int buffer_length)
+{
+ unsigned int i, cin, length;
+
+ for (i = 0; i + 3 < buffer_length; i += 4) {
+ if (buffer[i] == 0 && i > 0)
+ break;
+ cin = buffer[i] & 0x0f;
+ if (ep->in_sysex &&
+ cin == ep->last_cin &&
+ (buffer[i + 1 + (cin == 0x6)] & 0x80) == 0)
+ cin = 0x4;
+#if 0
+ if (buffer[i + 1] == 0x90) {
+ /*
+ * Either a corrupted running status or a real note-on
+ * message; impossible to detect reliably.
+ */
+ }
+#endif
+ length = snd_usbmidi_cin_length[cin];
+ snd_usbmidi_input_data(ep, 0, &buffer[i + 1], length);
+ ep->in_sysex = cin == 0x4;
+ if (!ep->in_sysex)
+ ep->last_cin = cin;
+ }
+}
+
+/*
* CME protocol: like the standard protocol, but SysEx commands are sent as a
* single USB packet preceded by a 0x0F byte.
*/
@@ -660,6 +695,12 @@ static struct usb_protocol_ops snd_usbmidi_cme_ops = {
.output_packet = snd_usbmidi_output_standard_packet,
};
+static struct usb_protocol_ops snd_usbmidi_ch345_broken_sysex_ops = {
+ .input = ch345_broken_sysex_input,
+ .output = snd_usbmidi_standard_output,
+ .output_packet = snd_usbmidi_output_standard_packet,
+};
+
/*
* AKAI MPD16 protocol:
*
@@ -1341,6 +1382,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi,
* Various chips declare a packet size larger than 4 bytes, but
* do not actually work with larger packets:
*/
+ case USB_ID(0x0a67, 0x5011): /* Medeli DD305 */
case USB_ID(0x0a92, 0x1020): /* ESI M4U */
case USB_ID(0x1430, 0x474b): /* RedOctane GH MIDI INTERFACE */
case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */
@@ -1903,11 +1945,14 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi)
hostif = &intf->altsetting[1];
intfd = get_iface_desc(hostif);
+ /* If either or both of the endpoints support interrupt transfer,
+ * then use the alternate setting
+ */
if (intfd->bNumEndpoints != 2 ||
- (get_endpoint(hostif, 0)->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
- (get_endpoint(hostif, 1)->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+ !((get_endpoint(hostif, 0)->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT ||
+ (get_endpoint(hostif, 1)->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
return;
dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
@@ -2375,6 +2420,10 @@ int snd_usbmidi_create(struct snd_card *card,
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
break;
+ case QUIRK_MIDI_CH345:
+ umidi->usb_protocol_ops = &snd_usbmidi_ch345_broken_sysex_ops;
+ err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
+ break;
default:
dev_err(&umidi->dev->dev, "invalid quirk type %d\n",
quirk->type);
@@ -2405,7 +2454,6 @@ int snd_usbmidi_create(struct snd_card *card,
else
err = snd_usbmidi_create_endpoints(umidi, endpoints);
if (err < 0) {
- snd_usbmidi_free(umidi);
return err;
}
diff --git a/kernel/sound/usb/mixer.c b/kernel/sound/usb/mixer.c
index cd8ed2e39..4f8575700 100644
--- a/kernel/sound/usb/mixer.c
+++ b/kernel/sound/usb/mixer.c
@@ -282,6 +282,21 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
return val;
}
+static int uac2_ctl_value_size(int val_type)
+{
+ switch (val_type) {
+ case USB_MIXER_S32:
+ case USB_MIXER_U32:
+ return 4;
+ case USB_MIXER_S16:
+ case USB_MIXER_U16:
+ return 2;
+ default:
+ return 1;
+ }
+ return 0; /* unreachable */
+}
+
/*
* retrieve a mixer value
@@ -296,14 +311,11 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
int timeout = 10;
int idx = 0, err;
- err = snd_usb_autoresume(chip);
+ err = snd_usb_lock_shutdown(chip);
if (err < 0)
return -EIO;
- down_read(&chip->shutdown_rwsem);
while (timeout-- > 0) {
- if (chip->shutdown)
- break;
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
@@ -319,8 +331,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
err = -EINVAL;
out:
- up_read(&chip->shutdown_rwsem);
- snd_usb_autosuspend(chip);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -328,14 +339,14 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
int validx, int *value_ret)
{
struct snd_usb_audio *chip = cval->head.mixer->chip;
- unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */
+ unsigned char buf[4 + 3 * sizeof(__u32)]; /* enough space for one range */
unsigned char *val;
int idx = 0, ret, size;
__u8 bRequest;
if (request == UAC_GET_CUR) {
bRequest = UAC2_CS_CUR;
- size = sizeof(__u16);
+ size = uac2_ctl_value_size(cval->val_type);
} else {
bRequest = UAC2_CS_RANGE;
size = sizeof(buf);
@@ -343,21 +354,15 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
memset(buf, 0, sizeof(buf));
- ret = snd_usb_autoresume(chip) ? -EIO : 0;
+ ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
if (ret)
goto error;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown) {
- ret = -ENODEV;
- } else {
- idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
- ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
+ idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
validx, idx, buf, size);
- }
- up_read(&chip->shutdown_rwsem);
- snd_usb_autosuspend(chip);
+ snd_usb_unlock_shutdown(chip);
if (ret < 0) {
error:
@@ -446,7 +451,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set)
{
struct snd_usb_audio *chip = cval->head.mixer->chip;
- unsigned char buf[2];
+ unsigned char buf[4];
int idx = 0, val_len, err, timeout = 10;
validx += cval->idx_off;
@@ -454,8 +459,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
if (cval->head.mixer->protocol == UAC_VERSION_1) {
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
} else { /* UAC_VERSION_2 */
- /* audio class v2 controls are always 2 bytes in size */
- val_len = sizeof(__u16);
+ val_len = uac2_ctl_value_size(cval->val_type);
/* FIXME */
if (request != UAC_SET_CUR) {
@@ -469,13 +473,14 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
value_set = convert_bytes_value(cval, value_set);
buf[0] = value_set & 0xff;
buf[1] = (value_set >> 8) & 0xff;
- err = snd_usb_autoresume(chip);
+ buf[2] = (value_set >> 16) & 0xff;
+ buf[3] = (value_set >> 24) & 0xff;
+
+ err = snd_usb_lock_shutdown(chip);
if (err < 0)
return -EIO;
- down_read(&chip->shutdown_rwsem);
+
while (timeout-- > 0) {
- if (chip->shutdown)
- break;
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
if (snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), request,
@@ -490,8 +495,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
err = -EINVAL;
out:
- up_read(&chip->shutdown_rwsem);
- snd_usb_autosuspend(chip);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -715,15 +719,21 @@ static int check_input_term(struct mixer_build *state, int id,
term->name = d->iTerminal;
} else { /* UAC_VERSION_2 */
struct uac2_input_terminal_descriptor *d = p1;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le32_to_cpu(d->bmChannelConfig);
- term->name = d->iTerminal;
- /* call recursively to get the clock selectors */
+ /* call recursively to verify that the
+ * referenced clock entity is valid */
err = check_input_term(state, d->bCSourceID, term);
if (err < 0)
return err;
+
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the
+ * recursion calls */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le32_to_cpu(d->bmChannelConfig);
+ term->name = d->iTerminal;
}
return 0;
case UAC_FEATURE_UNIT: {
@@ -798,24 +808,25 @@ static int check_input_term(struct mixer_build *state, int id,
/* feature unit control information */
struct usb_feature_control_info {
const char *name;
- unsigned int type; /* control type (mute, volume, etc.) */
+ int type; /* data type for uac1 */
+ int type_uac2; /* data type for uac2 if different from uac1, else -1 */
};
static struct usb_feature_control_info audio_feature_info[] = {
- { "Mute", USB_MIXER_INV_BOOLEAN },
- { "Volume", USB_MIXER_S16 },
- { "Tone Control - Bass", USB_MIXER_S8 },
- { "Tone Control - Mid", USB_MIXER_S8 },
- { "Tone Control - Treble", USB_MIXER_S8 },
- { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */
- { "Auto Gain Control", USB_MIXER_BOOLEAN },
- { "Delay Control", USB_MIXER_U16 },
- { "Bass Boost", USB_MIXER_BOOLEAN },
- { "Loudness", USB_MIXER_BOOLEAN },
+ { "Mute", USB_MIXER_INV_BOOLEAN, -1 },
+ { "Volume", USB_MIXER_S16, -1 },
+ { "Tone Control - Bass", USB_MIXER_S8, -1 },
+ { "Tone Control - Mid", USB_MIXER_S8, -1 },
+ { "Tone Control - Treble", USB_MIXER_S8, -1 },
+ { "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemeted yet */
+ { "Auto Gain Control", USB_MIXER_BOOLEAN, -1 },
+ { "Delay Control", USB_MIXER_U16, USB_MIXER_U32 },
+ { "Bass Boost", USB_MIXER_BOOLEAN, -1 },
+ { "Loudness", USB_MIXER_BOOLEAN, -1 },
/* UAC2 specific */
- { "Input Gain Control", USB_MIXER_U16 },
- { "Input Gain Pad Control", USB_MIXER_BOOLEAN },
- { "Phase Inverter Control", USB_MIXER_BOOLEAN },
+ { "Input Gain Control", USB_MIXER_S16, -1 },
+ { "Input Gain Pad Control", USB_MIXER_S16, -1 },
+ { "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
};
/* private_free callback */
@@ -1215,6 +1226,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
int readonly_mask)
{
struct uac_feature_unit_descriptor *desc = raw_desc;
+ struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
int mapped_name = 0;
int nameid = uac_feature_unit_iFeature(desc);
@@ -1240,7 +1252,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = control;
cval->cmask = ctl_mask;
- cval->val_type = audio_feature_info[control-1].type;
+ ctl_info = &audio_feature_info[control-1];
+ if (state->mixer->protocol == UAC_VERSION_1)
+ cval->val_type = ctl_info->type;
+ else /* UAC_VERSION_2 */
+ cval->val_type = ctl_info->type_uac2 >= 0 ?
+ ctl_info->type_uac2 : ctl_info->type;
+
if (ctl_mask == 0) {
cval->channels = 1; /* master channel */
cval->master_readonly = readonly_mask;
@@ -1336,6 +1354,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
}
}
+ snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl);
+
range = (cval->max - cval->min) / cval->res;
/*
* Are there devices with volume range more than 255? I use a bit more
diff --git a/kernel/sound/usb/mixer.h b/kernel/sound/usb/mixer.h
index d3268f0ee..3417ef347 100644
--- a/kernel/sound/usb/mixer.h
+++ b/kernel/sound/usb/mixer.h
@@ -33,6 +33,8 @@ enum {
USB_MIXER_U8,
USB_MIXER_S16,
USB_MIXER_U16,
+ USB_MIXER_S32,
+ USB_MIXER_U32,
};
typedef void (*usb_mixer_elem_dump_func_t)(struct snd_info_buffer *buffer,
diff --git a/kernel/sound/usb/mixer_maps.c b/kernel/sound/usb/mixer_maps.c
index 6a803eff8..ddca65473 100644
--- a/kernel/sound/usb/mixer_maps.c
+++ b/kernel/sound/usb/mixer_maps.c
@@ -348,13 +348,6 @@ static struct usbmix_name_map bose_companion5_map[] = {
{ 0 } /* terminator */
};
-/* Dragonfly DAC 1.2, the dB conversion factor is 1 instead of 256 */
-static struct usbmix_dB_map dragonfly_1_2_dB = {0, 5000};
-static struct usbmix_name_map dragonfly_1_2_map[] = {
- { 7, NULL, .dB = &dragonfly_1_2_dB },
- { 0 } /* terminator */
-};
-
/*
* Control map entries
*/
@@ -470,11 +463,6 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x05a7, 0x1020),
.map = bose_companion5_map,
},
- {
- /* Dragonfly DAC 1.2 */
- .id = USB_ID(0x21b4, 0x0081),
- .map = dragonfly_1_2_map,
- },
{ 0 } /* terminator */
};
diff --git a/kernel/sound/usb/mixer_quirks.c b/kernel/sound/usb/mixer_quirks.c
index 337c317ea..279025650 100644
--- a/kernel/sound/usb/mixer_quirks.c
+++ b/kernel/sound/usb/mixer_quirks.c
@@ -37,6 +37,7 @@
#include <sound/control.h>
#include <sound/hwdep.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include "usbaudio.h"
#include "mixer.h"
@@ -308,11 +309,10 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
struct snd_usb_audio *chip = mixer->chip;
int err;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown) {
- err = -ENODEV;
- goto out;
- }
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
+
if (chip->usb_id == USB_ID(0x041e, 0x3042))
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x24,
@@ -329,8 +329,7 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
usb_sndctrlpipe(chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
value, index + 2, NULL, 0);
- out:
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -340,7 +339,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
int index = kcontrol->private_value & 0xff;
- int value = ucontrol->value.integer.value[0];
+ unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
int err;
@@ -441,16 +440,15 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
for (i = 0; jacks[i].name; ++i) {
snd_iprintf(buffer, "%s: ", jacks[i].name);
- down_read(&mixer->chip->shutdown_rwsem);
- if (mixer->chip->shutdown)
- err = 0;
- else
- err = snd_usb_ctl_msg(mixer->chip->dev,
+ err = snd_usb_lock_shutdown(mixer->chip);
+ if (err < 0)
+ return;
+ err = snd_usb_ctl_msg(mixer->chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0),
UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0,
jacks[i].unitid << 8, buf, 3);
- up_read(&mixer->chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(mixer->chip);
if (err == 3 && (buf[0] == 3 || buf[0] == 6))
snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
else
@@ -481,11 +479,9 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
int err;
unsigned char buf[2];
- down_read(&chip->shutdown_rwsem);
- if (mixer->chip->shutdown) {
- err = -ENODEV;
- goto out;
- }
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
buf[0] = 0x01;
buf[1] = value ? 0x02 : 0x01;
@@ -493,8 +489,7 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
0x0400, 0x0e00, buf, 2);
- out:
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -554,15 +549,14 @@ static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer,
struct snd_usb_audio *chip = mixer->chip;
int err;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown)
- err = -ENODEV;
- else
- err = snd_usb_ctl_msg(chip->dev,
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
+ err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x08,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
50, 0, &status, 1);
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -623,11 +617,9 @@ static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val)
int err;
unsigned char buff[3];
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown) {
- err = -ENODEV;
- goto err;
- }
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
/* Prepare for magic command to toggle clock source */
err = snd_usb_ctl_msg(chip->dev,
@@ -683,7 +675,7 @@ static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val)
goto err;
err:
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -778,15 +770,14 @@ static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
unsigned int pval = list->kctl->private_value;
int err;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown)
- err = -ENODEV;
- else
- err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
- (pval >> 16) & 0xff,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
- pval >> 24, pval & 0xffff, NULL, 0, 1000);
- up_read(&chip->shutdown_rwsem);
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
+ err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
+ (pval >> 16) & 0xff,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ pval >> 24, pval & 0xffff, NULL, 0, 1000);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -802,7 +793,7 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
return 0;
kcontrol->private_value &= ~(0xff << 24);
- kcontrol->private_value |= newval;
+ kcontrol->private_value |= (unsigned int)newval << 24;
err = snd_ni_update_cur_val(list);
return err < 0 ? err : 1;
}
@@ -944,18 +935,17 @@ static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
value[0] = pval >> 24;
value[1] = 0;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown)
- err = -ENODEV;
- else
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0),
- UAC_SET_CUR,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- pval & 0xff00,
- snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
- value, 2);
- up_read(&chip->shutdown_rwsem);
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ pval & 0xff00,
+ snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
+ value, 2);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -1519,11 +1509,9 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
unsigned char data[3];
int rate;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown) {
- err = -ENODEV;
- goto end;
- }
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
@@ -1551,7 +1539,7 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
err = 0;
end:
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -1562,11 +1550,9 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
u8 reg;
int err;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown) {
- err = -ENODEV;
- goto end;
- }
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
err = snd_usb_ctl_msg(chip->dev,
@@ -1594,7 +1580,7 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
goto end;
end:
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -1650,11 +1636,9 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
u8 reg = list->kctl->private_value;
int err;
- down_read(&chip->shutdown_rwsem);
- if (chip->shutdown) {
- err = -ENODEV;
- goto end;
- }
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0),
@@ -1665,8 +1649,7 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
NULL,
0);
- end:
- up_read(&chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(chip);
return err;
}
@@ -1843,3 +1826,39 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
}
}
+static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
+{
+ /* Approximation using 10 ranges based on output measurement on hw v1.2.
+ * This seems close to the cubic mapping e.g. alsamixer uses. */
+ static const DECLARE_TLV_DB_RANGE(scale,
+ 0, 1, TLV_DB_MINMAX_ITEM(-5300, -4970),
+ 2, 5, TLV_DB_MINMAX_ITEM(-4710, -4160),
+ 6, 7, TLV_DB_MINMAX_ITEM(-3884, -3710),
+ 8, 14, TLV_DB_MINMAX_ITEM(-3443, -2560),
+ 15, 16, TLV_DB_MINMAX_ITEM(-2475, -2324),
+ 17, 19, TLV_DB_MINMAX_ITEM(-2228, -2031),
+ 20, 26, TLV_DB_MINMAX_ITEM(-1910, -1393),
+ 27, 31, TLV_DB_MINMAX_ITEM(-1322, -1032),
+ 32, 40, TLV_DB_MINMAX_ITEM(-968, -490),
+ 41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
+ );
+
+ usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk\n");
+ kctl->tlv.p = scale;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+}
+
+void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval, int unitid,
+ struct snd_kcontrol *kctl)
+{
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
+ if (unitid == 7 && cval->min == 0 && cval->max == 50)
+ snd_dragonfly_quirk_db_scale(mixer, kctl);
+ break;
+ }
+}
+
diff --git a/kernel/sound/usb/mixer_quirks.h b/kernel/sound/usb/mixer_quirks.h
index bdbfab093..177c329cd 100644
--- a/kernel/sound/usb/mixer_quirks.h
+++ b/kernel/sound/usb/mixer_quirks.h
@@ -9,5 +9,9 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
int unitid);
+void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval, int unitid,
+ struct snd_kcontrol *kctl);
+
#endif /* SND_USB_MIXER_QUIRKS_H */
diff --git a/kernel/sound/usb/pcm.c b/kernel/sound/usb/pcm.c
index b4ef410e5..9245f52d4 100644
--- a/kernel/sound/usb/pcm.c
+++ b/kernel/sound/usb/pcm.c
@@ -80,7 +80,7 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
unsigned int hwptr_done;
subs = (struct snd_usb_substream *)substream->runtime->private_data;
- if (subs->stream->chip->shutdown)
+ if (atomic_read(&subs->stream->chip->shutdown))
return SNDRV_PCM_POS_XRUN;
spin_lock(&subs->lock);
hwptr_done = subs->hwptr_done;
@@ -391,6 +391,20 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
*/
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+ if ((is_playback && (attr != USB_ENDPOINT_SYNC_ASYNC)) ||
+ (!is_playback && (attr != USB_ENDPOINT_SYNC_ADAPTIVE))) {
+
+ /*
+ * In these modes the notion of sync_endpoint is irrelevant.
+ * Reset pointers to avoid using stale data from previously
+ * used settings, e.g. when configuration and endpoints were
+ * changed
+ */
+
+ subs->sync_endpoint = NULL;
+ subs->data_endpoint->sync_master = NULL;
+ }
+
err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr);
if (err < 0)
return err;
@@ -398,10 +412,17 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
if (altsd->bNumEndpoints < 2)
return 0;
- if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) ||
+ if ((is_playback && (attr == USB_ENDPOINT_SYNC_SYNC ||
+ attr == USB_ENDPOINT_SYNC_ADAPTIVE)) ||
(!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE))
return 0;
+ /*
+ * In case of illegal SYNC_NONE for OUT endpoint, we keep going to see
+ * if we don't find a sync endpoint, as on M-Audio Transit. In case of
+ * error fall back to SYNC mode and don't create sync endpoint
+ */
+
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
because there is a version of the SB Audigy 2 NX firmware lacking
@@ -415,6 +436,8 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
get_endpoint(alts, 1)->bmAttributes,
get_endpoint(alts, 1)->bLength,
get_endpoint(alts, 1)->bSynchAddress);
+ if (is_playback && attr == USB_ENDPOINT_SYNC_NONE)
+ return 0;
return -EINVAL;
}
ep = get_endpoint(alts, 1)->bEndpointAddress;
@@ -425,6 +448,8 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
"%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
fmt->iface, fmt->altsetting,
is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
+ if (is_playback && attr == USB_ENDPOINT_SYNC_NONE)
+ return 0;
return -EINVAL;
}
@@ -436,8 +461,11 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
implicit_fb ?
SND_USB_ENDPOINT_TYPE_DATA :
SND_USB_ENDPOINT_TYPE_SYNC);
- if (!subs->sync_endpoint)
+ if (!subs->sync_endpoint) {
+ if (is_playback && attr == USB_ENDPOINT_SYNC_NONE)
+ return 0;
return -EINVAL;
+ }
subs->data_endpoint->sync_master = subs->sync_endpoint;
@@ -707,12 +735,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- down_read(&subs->stream->chip->shutdown_rwsem);
- if (subs->stream->chip->shutdown)
- ret = -ENODEV;
- else
- ret = set_format(subs, fmt);
- up_read(&subs->stream->chip->shutdown_rwsem);
+ ret = snd_usb_lock_shutdown(subs->stream->chip);
+ if (ret < 0)
+ return ret;
+ ret = set_format(subs, fmt);
+ snd_usb_unlock_shutdown(subs->stream->chip);
if (ret < 0)
return ret;
@@ -735,13 +762,12 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
- down_read(&subs->stream->chip->shutdown_rwsem);
- if (!subs->stream->chip->shutdown) {
+ if (!snd_usb_lock_shutdown(subs->stream->chip)) {
stop_endpoints(subs, true);
snd_usb_endpoint_deactivate(subs->sync_endpoint);
snd_usb_endpoint_deactivate(subs->data_endpoint);
+ snd_usb_unlock_shutdown(subs->stream->chip);
}
- up_read(&subs->stream->chip->shutdown_rwsem);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
@@ -763,11 +789,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
return -ENXIO;
}
- down_read(&subs->stream->chip->shutdown_rwsem);
- if (subs->stream->chip->shutdown) {
- ret = -ENODEV;
- goto unlock;
- }
+ ret = snd_usb_lock_shutdown(subs->stream->chip);
+ if (ret < 0)
+ return ret;
if (snd_BUG_ON(!subs->data_endpoint)) {
ret = -EIO;
goto unlock;
@@ -816,7 +840,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
ret = start_endpoints(subs, true);
unlock:
- up_read(&subs->stream->chip->shutdown_rwsem);
+ snd_usb_unlock_shutdown(subs->stream->chip);
return ret;
}
@@ -1218,9 +1242,11 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
stop_endpoints(subs, true);
- if (!as->chip->shutdown && subs->interface >= 0) {
+ if (subs->interface >= 0 &&
+ !snd_usb_lock_shutdown(subs->stream->chip)) {
usb_set_interface(subs->dev, subs->interface, 0);
subs->interface = -1;
+ snd_usb_unlock_shutdown(subs->stream->chip);
}
subs->pcm_substream = NULL;
@@ -1357,6 +1383,56 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
subs->hwptr_done++;
}
}
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
+ int offset, int stride, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer + offset,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + offset + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer + offset,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
+ struct urb *urb, int stride,
+ unsigned int bytes)
+{
+ __le32 packet_length;
+ int i;
+
+ /* Put __le32 length descriptor at start of each packet. */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int length = urb->iso_frame_desc[i].length;
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+
+ packet_length = cpu_to_le32(length);
+ offset += i * sizeof(packet_length);
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length += sizeof(packet_length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ copy_to_urb(subs, urb, offset + sizeof(packet_length),
+ stride, length);
+ }
+ /* Adjust transfer size accordingly. */
+ bytes += urb->number_of_packets * sizeof(packet_length);
+ return bytes;
}
static void prepare_playback_urb(struct snd_usb_substream *subs,
@@ -1434,27 +1510,17 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
}
subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
} else {
/* usual PCM */
- if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
- /* err, the transferred area goes over buffer boundary. */
- unsigned int bytes1 =
- runtime->buffer_size * stride - subs->hwptr_done;
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes1);
- memcpy(urb->transfer_buffer + bytes1,
- runtime->dma_area, bytes - bytes1);
- } else {
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes);
- }
-
- subs->hwptr_done += bytes;
+ if (!subs->tx_length_quirk)
+ copy_to_urb(subs, urb, 0, stride, bytes);
+ else
+ bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+ /* bytes is now amount of outgoing data */
}
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
-
/* update delay with exact number of samples queued */
runtime->delay = subs->last_delay;
runtime->delay += frames;
diff --git a/kernel/sound/usb/proc.c b/kernel/sound/usb/proc.c
index 5f761ab34..0ac89e294 100644
--- a/kernel/sound/usb/proc.c
+++ b/kernel/sound/usb/proc.c
@@ -46,14 +46,14 @@ static inline unsigned get_high_speed_hz(unsigned int usb_rate)
static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_audio *chip = entry->private_data;
- if (!chip->shutdown)
+ if (!atomic_read(&chip->shutdown))
snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
}
static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_audio *chip = entry->private_data;
- if (!chip->shutdown)
+ if (!atomic_read(&chip->shutdown))
snd_iprintf(buffer, "%04x:%04x\n",
USB_ID_VENDOR(chip->usb_id),
USB_ID_PRODUCT(chip->usb_id));
diff --git a/kernel/sound/usb/quirks-table.h b/kernel/sound/usb/quirks-table.h
index e4756651a..c60a776e8 100644
--- a/kernel/sound/usb/quirks-table.h
+++ b/kernel/sound/usb/quirks-table.h
@@ -2664,6 +2664,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
+ USB_DEVICE(0x1235, 0x000a),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Novation", */
+ /* .product_name = "Nocturn", */
+ .ifnum = 0,
+ .type = QUIRK_MIDI_RAW_BYTES
+ }
+},
+{
USB_DEVICE(0x1235, 0x000e),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "Novation", */
@@ -2820,6 +2829,17 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.idProduct = 0x1020,
},
+/* QinHeng devices */
+{
+ USB_DEVICE(0x1a86, 0x752d),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "QinHeng",
+ .product_name = "CH345",
+ .ifnum = 1,
+ .type = QUIRK_MIDI_CH345
+ }
+},
+
/* KeithMcMillen Stringport */
{
USB_DEVICE(0x1f38, 0x0001),
@@ -3182,10 +3202,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
{
/*
* ZOOM R16/24 in audio interface mode.
- * Mixer descriptors are garbage, further quirks will be needed
- * to make any of it functional, thus disabled for now.
- * Playback stream appears to start and run fine but no sound
- * is produced, so also disabled for now.
+ * Playback requires an extra four byte LE length indicator
+ * at the start of each isochronous packet. This quirk is
+ * enabled in create_standard_audio_quirk().
*/
USB_DEVICE(0x1686, 0x00dd),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -3193,14 +3212,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
- /* Mixer */
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE,
- },
- {
/* Playback */
.ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE,
},
{
/* Capture */
diff --git a/kernel/sound/usb/quirks.c b/kernel/sound/usb/quirks.c
index 00ebc0ca0..c458d60d5 100644
--- a/kernel/sound/usb/quirks.c
+++ b/kernel/sound/usb/quirks.c
@@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
struct usb_interface_descriptor *altsd;
int err;
+ if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
+ chip->tx_length_quirk = 1;
+
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
@@ -535,6 +538,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
[QUIRK_MIDI_CME] = create_any_midi_quirk,
[QUIRK_MIDI_AKAI] = create_any_midi_quirk,
[QUIRK_MIDI_FTDI] = create_any_midi_quirk,
+ [QUIRK_MIDI_CH345] = create_any_midi_quirk,
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
@@ -1117,10 +1121,13 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
switch (chip->usb_id) {
case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */
case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */
+ case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
return true;
}
return false;
@@ -1200,8 +1207,12 @@ void snd_usb_set_interface_quirk(struct usb_device *dev)
* "Playback Design" products need a 50ms delay after setting the
* USB interface.
*/
- if (le16_to_cpu(dev->descriptor.idVendor) == 0x23ba)
+ switch (le16_to_cpu(dev->descriptor.idVendor)) {
+ case 0x23ba: /* Playback Design */
+ case 0x0644: /* TEAC Corp. */
mdelay(50);
+ break;
+ }
}
void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
@@ -1216,6 +1227,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
+ /*
+ * "TEAC Corp." products need a 20ms delay after each
+ * class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
+ mdelay(20);
+
/* Marantz/Denon devices with USB DAC functionality need a delay
* after each class compliant request
*/
@@ -1264,6 +1283,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case USB_ID(0x20b1, 0x3008): /* iFi Audio micro/nano iDSD */
case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */
case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */
+ case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
if (fp->altsetting == 2)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
@@ -1271,6 +1291,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */
case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */
+ case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */
+ case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
if (fp->altsetting == 3)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
diff --git a/kernel/sound/usb/stream.c b/kernel/sound/usb/stream.c
index 310a3822d..8ee14f236 100644
--- a/kernel/sound/usb/stream.c
+++ b/kernel/sound/usb/stream.c
@@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
subs->direction = stream;
subs->dev = as->chip->dev;
subs->txfr_quirk = as->chip->txfr_quirk;
+ subs->tx_length_quirk = as->chip->tx_length_quirk;
subs->speed = snd_usb_get_speed(subs->dev);
subs->pkt_offset_adj = 0;
@@ -377,7 +378,15 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
snd_usb_init_substream(as, stream, fp);
- list_add(&as->list, &chip->pcm_list);
+ /*
+ * Keep using head insertion for M-Audio Audiophile USB (tm) which has a
+ * fix to swap capture stream order in conf/cards/USB-audio.conf
+ */
+ if (chip->usb_id == USB_ID(0x0763, 0x2003))
+ list_add(&as->list, &chip->pcm_list);
+ else
+ list_add_tail(&as->list, &chip->pcm_list);
+
chip->pcm_devs++;
snd_usb_proc_pcm_format_add(as);
diff --git a/kernel/sound/usb/usbaudio.h b/kernel/sound/usb/usbaudio.h
index 91d038043..b665d8555 100644
--- a/kernel/sound/usb/usbaudio.h
+++ b/kernel/sound/usb/usbaudio.h
@@ -37,12 +37,13 @@ struct snd_usb_audio {
struct usb_interface *pm_intf;
u32 usb_id;
struct mutex mutex;
- struct rw_semaphore shutdown_rwsem;
- unsigned int shutdown:1;
- unsigned int probing:1;
- unsigned int in_pm:1;
unsigned int autosuspended:1;
+ atomic_t active;
+ atomic_t shutdown;
+ atomic_t usage_count;
+ wait_queue_head_t shutdown_wait;
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
int num_interfaces;
int num_suspended_intf;
@@ -94,6 +95,7 @@ enum quirk_type {
QUIRK_MIDI_AKAI,
QUIRK_MIDI_US122L,
QUIRK_MIDI_FTDI,
+ QUIRK_MIDI_CH345,
QUIRK_AUDIO_STANDARD_INTERFACE,
QUIRK_AUDIO_FIXED_ENDPOINT,
QUIRK_AUDIO_EDIROL_UAXX,
@@ -115,4 +117,7 @@ struct snd_usb_audio_quirk {
#define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16))
#define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24))
+int snd_usb_lock_shutdown(struct snd_usb_audio *chip);
+void snd_usb_unlock_shutdown(struct snd_usb_audio *chip);
+
#endif /* __USBAUDIO_H */