* [PATCH - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore() in azx_pcm_trigger(). This prevents deadlock when an interrupt occurs, caused by chip->reg_lock contention.
[not found] <1342223070-25852-1-git-send-email-ian_minett@creativelabs.com>
@ 2012-07-13 23:44 ` ian_minett
2012-07-16 10:18 ` [PATCH - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features Takashi Iwai
1 sibling, 0 replies; 21+ messages in thread
From: ian_minett @ 2012-07-13 23:44 UTC (permalink / raw)
To: patch; +Cc: alsa-devel, Ian Minett
From: Ian Minett <ian_minett@creativelabs.com>
Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 796472d..0ccffde 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -551,6 +551,7 @@ enum {
(AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
AZX_DCAPS_ALIGN_BUFSIZE)
+/* quirks for Creative CTHDA */
#define AZX_DCAPS_PRESET_CTHDA \
(AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
@@ -1930,6 +1931,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_substream *s;
int rstart = 0, start, nsync = 0, sbits = 0;
int nwait, timeout;
+ unsigned long flags;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -1956,7 +1958,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_trigger_done(s, substream);
}
- spin_lock(&chip->reg_lock);
+ spin_lock_irqsave(&chip->reg_lock, flags);
if (nsync > 1) {
/* first, set SYNC bits of corresponding streams */
if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
@@ -1980,7 +1982,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
azx_dev->running = start;
}
- spin_unlock(&chip->reg_lock);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
if (start) {
if (nsync == 1)
return 0;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
[not found] <1342223070-25852-1-git-send-email-ian_minett@creativelabs.com>
2012-07-13 23:44 ` [PATCH - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore() in azx_pcm_trigger(). This prevents deadlock when an interrupt occurs, caused by chip->reg_lock contention ian_minett
@ 2012-07-16 10:18 ` Takashi Iwai
2012-07-25 18:01 ` [PATCHv2 " Ian Minett
1 sibling, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-07-16 10:18 UTC (permalink / raw)
To: ian_minett; +Cc: alsa-devel
At Fri, 13 Jul 2012 16:44:28 -0700,
ian_minett@creativelabs.com wrote:
>
> From: Ian Minett <ian_minett@creativelabs.com>
>
> Thanks very much for the feedback on the last submission, apologies for the
> delayed response. Please find attached the new set of patches for updating
> the CA0132 codec, which takes many of the suggestions into account.
>
> - Ian
>
> ---
>
> ALSA: Update Creative CA0132 HDA codec to introduce DSP features.
>
> patch_ca0132.c:
> - Add DSP support to codec.
> - 3 capture subdevices supported: Analog Mic1/Digital Mic,
> Analog Mic2 and What U Hear.
> - Add jack detection kcontrol
> - Use request_firmware() for loading DSP binaries.
Ideally, these should be split to each patch. Though I see it's not
so trivial in this case...
But please provide more technical details. I can only guess what's
VNID and what's effect module ID. Give more descriptions.
> hda_intel.c:
> - in azx_pcm_trigger(), change spin_lock()/spin_unlock() pair to
> spin_lock_irqsave()/spin_unlock_irqrestore(). This still seems to be
> necessary - if an interrupt occurs after azx_stream_start() and before
> spin_unlock(), it will cause deadlock as azx_interrupt() also acquires
> chip->reg_lock.
Oh, this is basically because you are doing pretty bad things: calling
PCM trigger callback from the codec driver. What's the reason behind
it?
Since the whole patch lacks the big and small pictures, it's hard to
judge...
> ctefx.bin:
> - DSP binary for firmware repository, for driver to load from
> /lib/firmware directory.
Please submit the patch for adding this, too. Or, at least, give a
kernel config to build without the firmware.
Otherwise you'll break the driver.
Looking though the code, the biggest problem is that you are using
float in the code. This is now allowed in the kernel at all.
You have to rewrite without float type -- i.e. never use "float" in
the code, for both fields/arguments and computations.
Also, some other review comments below:
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index d0d3540..c7cdfe1 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -1,5 +1,5 @@
> /*
> - * HD audio interface patch for Creative CA0132 chip
> + * HD audio interface patch for Creative Sound Core3D chip
> *
> * Copyright (c) 2011, Creative Technology Ltd.
> *
> @@ -25,18 +25,470 @@
> #include <linux/delay.h>
> #include <linux/slab.h>
> #include <linux/pci.h>
> -#include <linux/mutex.h>
> #include <linux/module.h>
> +#include <linux/firmware.h>
> #include <sound/core.h>
> +#include <sound/tlv.h>
> #include "hda_codec.h"
> #include "hda_local.h"
> +#include "hda_jack.h"
> #include "hda_auto_parser.h"
>
> +#include "ca0132_regs.h"
> +
> +/* Enable this to see more debug messages. */
> +/*#define ENABLE_CA0132_DEBUG*/
> +/* Enable this to see controls for tuning purpose. */
> +/*#define ENABLE_TUNING_CONTROLS*/
> +
> +#define FLOAT_ZERO 0x00000000
> +#define FLOAT_ONE 0x3f800000
> +#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 SUCCEEDED(_x) (((int)(_x)) >= 0)
> +#define FAILED(_x) (((int)(_x)) < 0)
> +#define CT_OK (0)
> +#define CT_FAIL (-1)
Please avoid home-baked macros like this.
Usually a function returns zero for succcess and a negative error
code. No need to define special CT_XX macros.
> +
> +#define MIN(x, y) (((x) < (y)) ? (x) : (y))
> +#define MAX(x, y) (((x) > (y)) ? (x) : (y))
Use the standard min() and max() macros.
> +#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
> +#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
> +
> +#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8
> +#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
> +#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2
> +
> +#define MASTERCONTROL 0x80
> +#define MASTERCONTROL_ALLOC_DMA_CHAN 9
> +#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 22
> +
> #define WIDGET_CHIP_CTRL 0x15
> #define WIDGET_DSP_CTRL 0x16
>
> -#define WUH_MEM_CONNID 10
> -#define DSP_MEM_CONNID 16
> +#define MEM_CONNID_MICIN1 3
> +#define MEM_CONNID_MICIN2 5
> +#define MEM_CONNID_MICOUT1 12
> +#define MEM_CONNID_MICOUT2 14
> +#define MEM_CONNID_WUH 10
> +#define MEM_CONNID_DSP 16
> +#define MEM_CONNID_DMIC 100
> +
> +#define SCP_SET 0
> +#define SCP_GET 1
> +
> +#define SPEQ_FILE "ctspeq.bin"
> +#define SPEQ_SIZE 0x2040
> +#define EFX_FILE "ctefx.bin"
> +#define EFX_SIZE 0xA003C
Don't forget to add MODULE_FIRMWARE() with these files, too.
I stop at this point... Too big to review...
thanks,
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-07-16 10:18 ` [PATCH - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features Takashi Iwai
@ 2012-07-25 18:01 ` Ian Minett
2012-07-25 18:01 ` [PATCHv2 - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore() Ian Minett
2012-07-26 8:06 ` [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features Takashi Iwai
0 siblings, 2 replies; 21+ messages in thread
From: Ian Minett @ 2012-07-25 18:01 UTC (permalink / raw)
To: patch; +Cc: alsa-devel, Ian Minett
From: Ian Minett <ian_minett@creativelabs.com>
Hi,
Thanks again for the feedback - we've reworked the patch accordingly, to remove
floats, tidy the use of macros and add the MODULE_FIRMWARE() lines.
You are right - since it is a pretty big change introducing all the DSP
functionality it isn't trivial to break it into multiple small patches.
I've added more info on what has been changed below in the git commit message.
The reason we need to modify the trigger callback in hda_intel.c is that the DSP
code is downloaded from the host (driver) to the CA0132 chip via the HD-link.
This means that the trigger callback is called from the codec driver.
I will submit the DSP firmware blob (ctefx.bin) as a separate patch against the
alsa-firmware repository, for intended upstreaming to linux-firmware package.
The CA0132 codec uses request_firmware() loader to find the DSP binary in the
/lib/firmware directory (the firmware isn't required for building the CA0132
module). If this isn't the correct way to submit firmware binaries, we'd
appreciate any info on what the correct method for this is.
Thank you very much,
- Ian
---
ALSA: Update Creative CA0132 HDA codec to introduce DSP features.
patch_ca0132.c:
- Add DSP support to codec.
Playback Effects and controls:
- Play Enhancement On/Off
This is the master control for Surround, Crystalizer,
Dialog Plus, Smart Volume, Equalizer and X-Bass.
- Surround On/Off
- Crystalizer On/Off
- Dialog Plus On/Off
- Smart Volume On/Off
- Equalizer On/Off
- X-Bass On/Off
Capture Effects and controls:
- Applied to Analog Mic-In1 / Stereo Digital-Mic.
- CrystalVoice On/Off
Master control for Acoustic Echo Cancellation,
Voice Focus, Mic-Smart Volume, Voice FX.
- Acoustic Echo Cancellation On/Off
- Voice Focus (Beam Forming) On/Off
Apply to Stereo Digital Mic, not Analog Mic-In1.
- Smart Volume (Mic SVM) On/Off
- Voice FX (Voice Morphing)
Morph Effects preset selection include:
- Neutral,
- Male2Female,
- Female2Male,
- Scrappy Kid,
- Elderly,
- Orc,
- Elf,
- Dwarf,
- Alien Brute,
- Robot,
- Marine,
- Emo,
- Deep Voice,
- Munchkin
- Add VNIDs (Virtual Node IDs) to denote controls that are not in the
physical codec node ID.
- Add effect module IDs to identify individual effect modules present
in the DSP.
- Playback Device: One 2-ch device for:
* Built-in speaker/Headphone out
- Mutually exclusive, and independent volume/mute control.
- Only one will be active at any instance.
- Headphone Out will take precedence over Built-in speaker when
it's plugged in or detected.
- Capture subdevices: 3 supported:
* Analog Mic1/Digital Mic
- Mutually exclusive, and independent volume/select control.
- Only one will be active at any instance.
- Analog Mic-In1 has +30dB Mic-Boost on/off control.
- Analog-Mic1 In will take precedence over Built-in Stereo
Digital-Mic when it's plugged in or detected.
* Analog Mic2
* What U Hear.
- Add jack detection kcontrol
- Use request_firmware() for loading DSP binaries from /lib/firmware.
- Mixer controls:
- Volume level and Mute controls.
- Capture select controls.
- Mic-Boost control.
- Effects controls (as above).
hda_intel.c:
- in azx_pcm_trigger(), change spin_lock()/spin_unlock() pair to
spin_lock_irqsave()/spin_unlock_irqrestore(). This is necessary for DSP code
downloading via HD-link. If an interrupt occurs after azx_stream_start() and
before spin_unlock(), it will cause deadlock as azx_interrupt() also acquires
chip->reg_lock.
Developed and maintained by Creative Labs, Inc.
Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h
new file mode 100644
index 0000000..831ca9c
--- /dev/null
+++ b/sound/pci/hda/ca0132_regs.h
@@ -0,0 +1,409 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip.
+ * CA0132 registers defines.
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * This driver 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 driver 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
+ */
+
+#ifndef __CA0132_REGS_H
+#define __CA0312_REGS_H
+
+#define DSP_CHIP_OFFSET 0x100000
+#define DSP_DBGCNTL_MODULE_OFFSET 0xE30
+#define DSP_DBGCNTL_INST_OFFSET \
+ (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET)
+
+#define DSP_DBGCNTL_EXEC_LOBIT 0x0
+#define DSP_DBGCNTL_EXEC_HIBIT 0x3
+#define DSP_DBGCNTL_EXEC_MASK 0xF
+
+#define DSP_DBGCNTL_SS_LOBIT 0x4
+#define DSP_DBGCNTL_SS_HIBIT 0x7
+#define DSP_DBGCNTL_SS_MASK 0xF0
+
+#define DSP_DBGCNTL_STATE_LOBIT 0xA
+#define DSP_DBGCNTL_STATE_HIBIT 0xD
+#define DSP_DBGCNTL_STATE_MASK 0x3C00
+
+#define XRAM_CHIP_OFFSET 0x0
+#define XRAM_XRAM_CHANNEL_COUNT 0xE000
+#define XRAM_XRAM_MODULE_OFFSET 0x0
+#define XRAM_XRAM_CHAN_INCR 4
+#define XRAM_XRAM_INST_OFFSET(_chan) \
+ (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \
+ (_chan * XRAM_XRAM_CHAN_INCR))
+
+#define YRAM_CHIP_OFFSET 0x40000
+#define YRAM_YRAM_CHANNEL_COUNT 0x8000
+#define YRAM_YRAM_MODULE_OFFSET 0x0
+#define YRAM_YRAM_CHAN_INCR 4
+#define YRAM_YRAM_INST_OFFSET(_chan) \
+ (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \
+ (_chan * YRAM_YRAM_CHAN_INCR))
+
+#define UC_CHIP_OFFSET 0x80000
+#define UC_UC_CHANNEL_COUNT 0x10000
+#define UC_UC_MODULE_OFFSET 0x0
+#define UC_UC_CHAN_INCR 4
+#define UC_UC_INST_OFFSET(_chan) \
+ (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \
+ (_chan * UC_UC_CHAN_INCR))
+
+#define AXRAM_CHIP_OFFSET 0x3C000
+#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000
+#define AXRAM_AXRAM_MODULE_OFFSET 0x0
+#define AXRAM_AXRAM_CHAN_INCR 4
+#define AXRAM_AXRAM_INST_OFFSET(_chan) \
+ (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \
+ (_chan * AXRAM_AXRAM_CHAN_INCR))
+
+#define AYRAM_CHIP_OFFSET 0x78000
+#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000
+#define AYRAM_AYRAM_MODULE_OFFSET 0x0
+#define AYRAM_AYRAM_CHAN_INCR 4
+#define AYRAM_AYRAM_INST_OFFSET(_chan) \
+ (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \
+ (_chan * AYRAM_AYRAM_CHAN_INCR))
+
+#define DSPDMAC_CHIP_OFFSET 0x110000
+#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12
+#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00
+#define DSPDMAC_DMACFG_CHAN_INCR 0x10
+#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DMACFG_CHAN_INCR))
+
+#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0
+#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10
+#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF
+#define DSPDMAC_DMACFG_LP_LOBIT 0x11
+#define DSPDMAC_DMACFG_LP_HIBIT 0x11
+#define DSPDMAC_DMACFG_LP_MASK 0x20000
+
+#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_MASK 0x40000
+
+#define DSPDMAC_DMACFG_DWR_LOBIT 0x13
+#define DSPDMAC_DMACFG_DWR_HIBIT 0x13
+#define DSPDMAC_DMACFG_DWR_MASK 0x80000
+
+#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14
+#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17
+#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000
+
+#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18
+#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19
+#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000
+
+#define DSPDMAC_DMACFG_LK_LOBIT 0x1A
+#define DSPDMAC_DMACFG_LK_HIBIT 0x1A
+#define DSPDMAC_DMACFG_LK_MASK 0x4000000
+
+#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B
+#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F
+#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000
+
+#define DSPDMAC_DMACFG_LP_SINGLE 0
+#define DSPDMAC_DMACFG_LP_LOOPING 1
+
+#define DSPDMAC_DMACFG_AINCR_XANDY 0
+#define DSPDMAC_DMACFG_AINCR_XORY 1
+
+#define DSPDMAC_DMACFG_DWR_DMA_RD 0
+#define DSPDMAC_DMACFG_DWR_DMA_WR 1
+
+#define DSPDMAC_DMACFG_AMODE_LINEAR 0
+#define DSPDMAC_DMACFG_AMODE_RSV1 1
+#define DSPDMAC_DMACFG_AMODE_WINTLV 2
+#define DSPDMAC_DMACFG_AMODE_GINTLV 3
+
+#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADROFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0
+#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF
+#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF
+
+#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10
+#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F
+#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000
+
+#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10
+
+#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA
+#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF
+
+#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB
+#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800
+
+#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A
+#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000
+
+#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B
+#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000
+
+#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9
+#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF
+
+#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA
+#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC
+#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00
+
+#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD
+#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000
+
+#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19
+#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000
+
+#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A
+#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C
+#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000
+
+#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D
+#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000
+
+#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08
+#define DSPDMAC_XFRCNT_CHAN_INCR 0x10
+
+#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_XFRCNT_CHAN_INCR))
+
+#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0
+#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF
+#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF
+
+#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10
+#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F
+#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000
+
+#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C
+#define DSPDMAC_IRQCNT_CHAN_INCR 0x10
+#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_IRQCNT_CHAN_INCR))
+
+#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0
+#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF
+#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF
+
+#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10
+#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F
+#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000
+
+#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12
+#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0
+#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4
+#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \
+ (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR))
+
+#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0
+#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F
+#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF
+
+#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0
+#define DSPDMAC_CHNLSTART_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0
+#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB
+#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC
+#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF
+#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000
+
+#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10
+#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B
+#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C
+#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F
+#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000
+
+#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4
+#define DSPDMAC_CHNLSTATUS_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0
+#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB
+#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000
+
+#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000
+
+#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000
+
+#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000
+
+#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10
+#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B
+#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C
+#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F
+#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000
+
+#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8
+#define DSPDMAC_CHNLPROP_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0
+#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB
+#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF
+
+#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000
+
+#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000
+
+#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000
+
+#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10
+#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B
+#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C
+#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F
+#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000
+
+#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC
+#define DSPDMAC_ACTIVE_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET)
+
+#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0
+#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB
+#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF
+
+#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC
+#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17
+#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000
+
+#define DSP_AUX_MEM_BASE 0xE000
+#define INVALID_CHIP_ADDRESS (~0UL)
+
+#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR)
+#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR)
+#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR)
+#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR)
+#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR)
+
+#define XEXT_SIZE (X_SIZE + AX_SIZE)
+#define YEXT_SIZE (Y_SIZE + AY_SIZE)
+
+#define U64K 0x10000UL
+
+#define X_END (XRAM_CHIP_OFFSET + X_SIZE)
+#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE)
+#define AX_END (XRAM_CHIP_OFFSET + U64K*4)
+
+#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE)
+#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE)
+#define AY_END (YRAM_CHIP_OFFSET + U64K*4)
+
+#define UC_END (UC_CHIP_OFFSET + UC_SIZE)
+
+#define X_RANGE_MAIN(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END))
+#define X_RANGE_AUX(a, s) \
+ (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+#define X_RANGE_EXT(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT))
+#define X_RANGE_ALL(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+
+#define Y_RANGE_MAIN(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END))
+#define Y_RANGE_AUX(a, s) \
+ (((a) >= Y_END) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+#define Y_RANGE_EXT(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT))
+#define Y_RANGE_ALL(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+
+#define UC_RANGE(a, s) \
+ (((a) >= UC_CHIP_OFFSET) && \
+ ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END))
+
+#define X_OFF(a) \
+ (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR)
+#define AX_OFF(a) \
+ (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \
+ AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR)
+
+#define Y_OFF(a) \
+ (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR)
+#define AY_OFF(a) \
+ (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \
+ AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR)
+
+#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR)
+
+#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a))
+#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a))
+
+#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a))
+#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a))
+
+#endif
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index d0d3540..825f5b4 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -1,5 +1,5 @@
/*
- * HD audio interface patch for Creative CA0132 chip
+ * HD audio interface patch for Creative Sound Core3D chip
*
* Copyright (c) 2011, Creative Technology Ltd.
*
@@ -25,18 +25,469 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/mutex.h>
#include <linux/module.h>
+#include <linux/firmware.h>
#include <sound/core.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_jack.h"
#include "hda_auto_parser.h"
+#include "ca0132_regs.h"
+
+/* Enable this to see more debug messages. */
+/*#define ENABLE_CA0132_DEBUG*/
+/* Enable this to see controls for tuning purpose. */
+/*#define ENABLE_TUNING_CONTROLS*/
+
+#define FLOAT_ZERO 0x00000000
+#define FLOAT_ONE 0x3f800000
+#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 SUCCEEDED(_x) (((int)(_x)) >= 0)
+#define FAILED(_x) (((int)(_x)) < 0)
+
+#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
+#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
+
+#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8
+#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
+#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2
+
+#define MASTERCONTROL 0x80
+#define MASTERCONTROL_ALLOC_DMA_CHAN 9
+#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 22
+
#define WIDGET_CHIP_CTRL 0x15
#define WIDGET_DSP_CTRL 0x16
-#define WUH_MEM_CONNID 10
-#define DSP_MEM_CONNID 16
+#define MEM_CONNID_MICIN1 3
+#define MEM_CONNID_MICIN2 5
+#define MEM_CONNID_MICOUT1 12
+#define MEM_CONNID_MICOUT2 14
+#define MEM_CONNID_WUH 10
+#define MEM_CONNID_DSP 16
+#define MEM_CONNID_DMIC 100
+
+#define SCP_SET 0
+#define SCP_GET 1
+
+#define SPEQ_FILE "ctspeq.bin"
+#define SPEQ_SIZE 0x2040
+#define EFX_FILE "ctefx.bin"
+
+MODULE_FIRMWARE(SPEQ_FILE);
+MODULE_FIRMWARE(EFX_FILE);
+
+/* Debug message controls */
+#ifdef ENABLE_CA0132_DEBUG
+
+#define CTASSERT(x) \
+ { if (!(x)) snd_printdd(KERN_ERR "CTASSERT failed.\n"); }
+
+#define CA0132_LOG(fmt, args...) snd_printdd(fmt, ##args)
+#define CA0132_DSP_LOG(msg) snd_printdd(KERN_INFO "[%s]\n", msg)
+#define FAIL_MSG(n, s) fail_debug_out(n, s)
+
+static int fail_debug_out(int status, const char *s)
+{
+ snd_printdd(KERN_ERR "[%s]\n", s);
+ return status;
+}
+#else
+
+#define CTASSERT(x) do { } while (0)
+#define CA0132_LOG(fmt, args...) do { } while (0)
+#define CA0132_DSP_LOG(msg) do { } while (0)
+#define FAIL_MSG(n, s) fail_debug_out(n, s)
+
+static int fail_debug_out(int status, const char *s)
+{
+ return status;
+}
+#endif
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+enum {
+ SPEAKER_OUT,
+ HEADPHONE_OUT
+};
+
+enum {
+ DIGITAL_MIC,
+ LINE_MIC_IN
+};
+
+enum {
+#define VNODE_START_NID 0x80
+ VNID_SPK = VNODE_START_NID, /* Speaker vnid */
+ VNID_MIC,
+ VNID_HP_SEL,
+ VNID_AMIC1_SEL,
+ VNID_HP_ASEL,
+ VNID_AMIC1_ASEL,
+ VNODE_END_NID,
+#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID)
+
+#define EFFECT_START_NID 0x90
+#define OUT_EFFECT_START_NID EFFECT_START_NID
+ SURROUND = OUT_EFFECT_START_NID,
+ CRYSTALIZER,
+ DIALOG_PLUS,
+ SMART_VOLUME,
+ X_BASS,
+ EQUALIZER,
+ OUT_EFFECT_END_NID,
+#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID)
+
+#define IN_EFFECT_START_NID OUT_EFFECT_END_NID
+ ECHO_CANCELLATION = IN_EFFECT_START_NID,
+ VOICE_FOCUS,
+ MIC_SVM,
+ IN_EFFECT_END_NID,
+#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID)
+
+ VOICEFX = IN_EFFECT_END_NID,
+ PLAY_ENHANCEMENT,
+ CRYSTAL_VOICE,
+ EFFECT_END_NID
+#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
+};
+
+/* Effects values size*/
+#define EFFECT_VALS_MAX_COUNT 12
+
+struct ct_effect {
+ char name[44];
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ int params; /* number of default non-on/off params */
+ /*effect default values, 1st is on/off. */
+ unsigned int def_vals[EFFECT_VALS_MAX_COUNT];
+};
+
+static struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
+ { "Surround",
+ SURROUND,
+ 0x96,
+ {0, 1},
+ 0,
+ 1,
+ {0x3F800000, 0x3F2B851F}
+ },
+ { "Crystalizer",
+ CRYSTALIZER,
+ 0x96,
+ {7, 8},
+ 0,
+ 1,
+ {0x3F800000, 0x3F266666}
+ },
+ { "Dialog Plus",
+ DIALOG_PLUS,
+ 0x96,
+ {2, 3},
+ 0,
+ 1,
+ {0x00000000, 0x3F000000}
+ },
+ { "Smart Volume",
+ SMART_VOLUME,
+ 0x96,
+ {4, 5, 6},
+ 0,
+ 2,
+ {0x3F800000, 0x3F3D70A4, 0x00000000}
+ },
+ { "X-Bass",
+ X_BASS,
+ 0x96,
+ {24, 23, 25},
+ 0,
+ 2,
+ {0x3F800000, 0x42A00000, 0x3F000000}
+ },
+ { "Equalizer",
+ EQUALIZER,
+ 0x96,
+ {9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20},
+ 0,
+ 11,
+ {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000}
+ },
+ { "Echo Cancellation",
+ ECHO_CANCELLATION,
+ 0x95,
+ {0, 1, 2, 3},
+ 1,
+ 3,
+ {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000}
+ },
+ { "Voice Focus",
+ VOICE_FOCUS,
+ 0x95,
+ {6, 7, 8, 9},
+ 1,
+ 3,
+ {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000}
+ },
+ { "Mic SVM",
+ MIC_SVM,
+ 0x95,
+ {44, 45},
+ 1,
+ 1,
+ {0x00000000, 0x3F3D70A4}
+ },
+ { "VoiceFX",
+ VOICEFX,
+ 0x95,
+ {10, 11, 12, 13, 14, 15, 16, 17, 18},
+ 1,
+ 8,
+ {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000, 0x3F800000,
+ 0x3F800000, 0x3F800000, 0x00000000, 0x00000000}
+ }
+};
+
+
+/* Tuning controls */
+#ifdef ENABLE_TUNING_CONTROLS
+
+enum {
+#define TUNING_CTL_START_NID 0xC0
+ WEDGE_ANGLE = TUNING_CTL_START_NID,
+ SVM_LEVEL,
+ EQUALIZER_BAND_0,
+ EQUALIZER_BAND_1,
+ EQUALIZER_BAND_2,
+ EQUALIZER_BAND_3,
+ EQUALIZER_BAND_4,
+ EQUALIZER_BAND_5,
+ EQUALIZER_BAND_6,
+ EQUALIZER_BAND_7,
+ EQUALIZER_BAND_8,
+ EQUALIZER_BAND_9,
+ TUNING_CTL_END_NID
+#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID)
+};
+
+struct ct_tuning_ctl {
+ char name[44];
+ hda_nid_t parent_nid;
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int req; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ unsigned int def_val;/*effect default values*/
+};
+
+static struct ct_tuning_ctl ca0132_tuning_ctls[] = {
+ { "Wedge Angle",
+ VOICE_FOCUS,
+ WEDGE_ANGLE,
+ 0x95,
+ 8,
+ 1,
+ 0x41F00000
+ },
+ { "SVM Level",
+ MIC_SVM,
+ SVM_LEVEL,
+ 0x95,
+ 45,
+ 1,
+ 0x3F3D70A4
+ },
+ { "EQ Band0",
+ EQUALIZER,
+ EQUALIZER_BAND_0,
+ 0x96,
+ 11,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band1",
+ EQUALIZER,
+ EQUALIZER_BAND_1,
+ 0x96,
+ 12,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band2",
+ EQUALIZER,
+ EQUALIZER_BAND_2,
+ 0x96,
+ 13,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band3",
+ EQUALIZER,
+ EQUALIZER_BAND_3,
+ 0x96,
+ 14,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band4",
+ EQUALIZER,
+ EQUALIZER_BAND_4,
+ 0x96,
+ 15,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band5",
+ EQUALIZER,
+ EQUALIZER_BAND_5,
+ 0x96,
+ 16,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band6",
+ EQUALIZER,
+ EQUALIZER_BAND_6,
+ 0x96,
+ 17,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band7",
+ EQUALIZER,
+ EQUALIZER_BAND_7,
+ 0x96,
+ 18,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band8",
+ EQUALIZER,
+ EQUALIZER_BAND_8,
+ 0x96,
+ 19,
+ 0,
+ 0x00000000
+ },
+ { "EQ Band9",
+ EQUALIZER,
+ EQUALIZER_BAND_9,
+ 0x96,
+ 20,
+ 0,
+ 0x00000000
+ }
+};
+#endif
+
+/* Voice FX Presets */
+#define VOICEFX_MAX_PARAM_COUNT 9
+
+struct ct_voicefx {
+ char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_voicefx_preset {
+ char *name; /*preset name*/
+ unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
+};
+
+struct ct_voicefx ca0132_voicefx = {
+ "VoiceFX Capture Switch",
+ VOICEFX,
+ 0x95,
+ {10, 11, 12, 13, 14, 15, 16, 17, 18}
+};
+
+struct ct_voicefx_preset ca0132_voicefx_presets[] = {
+ { "Neutral",
+ { 0x00000000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "Female2Male",
+ { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F19999A, 0x3F866666,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "Male2Female",
+ { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x450AC000, 0x4017AE14, 0x3F6B851F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "ScrappyKid",
+ { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x40400000, 0x3F28F5C3,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "Elderly",
+ { 0x3F800000, 0x44324000, 0x44BB8000,
+ 0x44E10000, 0x3FB33333, 0x3FB9999A,
+ 0x3F800000, 0x3E3A2E43, 0x00000000 }
+ },
+ { "Org",
+ { 0x3F800000, 0x43EA0000, 0x44A52000,
+ 0x45098000, 0x3F266666, 0x3FC00000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "Elf",
+ { 0x3F800000, 0x43C70000, 0x44AE6000,
+ 0x45193000, 0x3F8E147B, 0x3F75C28F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "Dwarf",
+ { 0x3F800000, 0x43930000, 0x44BEE000,
+ 0x45007000, 0x3F451EB8, 0x3F7851EC,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "AlienBrute",
+ { 0x3F800000, 0x43BFC5AC, 0x44B28FDF,
+ 0x451F6000, 0x3F266666, 0x3FA7D945,
+ 0x3F800000, 0x3CF5C28F, 0x00000000 }
+ },
+ { "Robot",
+ { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3FB2718B, 0x3F800000,
+ 0xBC07010E, 0x00000000, 0x00000000 }
+ },
+ { "Marine",
+ { 0x3F800000, 0x43C20000, 0x44906000,
+ 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71,
+ 0x3F0A3D71, 0x00000000, 0x00000000 }
+ },
+ { "Emo",
+ { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3E4CCCCD, 0x00000000, 0x00000000 }
+ },
+ { "DeepVoice",
+ { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF,
+ 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { "Munchkin",
+ { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F1A043C, 0x3F800000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ }
+};
enum hda_cmd_vendor_io {
/* for DspIO node */
@@ -62,7 +513,11 @@ enum hda_cmd_vendor_io {
VENDOR_CHIPIO_HIC_POST_READ = 0x702,
VENDOR_CHIPIO_HIC_READ_DATA = 0xF03,
+ VENDOR_CHIPIO_8051_DATA_WRITE = 0x707,
+ VENDOR_CHIPIO_8051_DATA_READ = 0xF07,
+
VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A,
+ VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A,
VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C,
VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C,
@@ -70,23 +525,29 @@ enum hda_cmd_vendor_io {
VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
VENDOR_CHIPIO_FLAG_SET = 0x70F,
VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
- VENDOR_CHIPIO_PARAMETER_SET = 0x710,
- VENDOR_CHIPIO_PARAMETER_GET = 0xF10,
+ VENDOR_CHIPIO_PARAM_SET = 0x710,
+ VENDOR_CHIPIO_PARAM_GET = 0xF10,
VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711,
VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712,
VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12,
VENDOR_CHIPIO_PORT_FREE_SET = 0x713,
- VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17,
- VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717,
- VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
- VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
+ VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718,
+
+ VENDOR_CHIPIO_DMIC_CTL_SET = 0x788,
+ VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88,
+ VENDOR_CHIPIO_DMIC_PIN_SET = 0x789,
+ VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89,
+ VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A,
+ VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A,
+
+ VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D
};
-/*
- * Control flag IDs
- */
enum control_flag_id {
/* Connection manager stream setup is bypassed/enabled */
CONTROL_FLAG_C_MGR = 0,
@@ -131,7 +592,7 @@ enum control_flag_id {
/* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20,
/* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
- CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21,
/* ASI rate is 48kHz/96kHz */
CONTROL_FLAG_ASI_96KHZ = 22,
/* DAC power settings able to control attached ports no/yes */
@@ -142,10 +603,7 @@ enum control_flag_id {
CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
};
-/*
- * Control parameter IDs
- */
-enum control_parameter_id {
+enum control_param_id {
/* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
CONTROL_PARAM_SPDIF1_SOURCE = 2,
@@ -175,9 +633,6 @@ enum control_parameter_id {
CONTROL_PARAM_NODE_ID = 31
};
-/*
- * Dsp Io Status codes
- */
enum hda_vendor_status_dspio {
/* Success */
VENDOR_STATUS_DSPIO_OK = 0x00,
@@ -189,9 +644,6 @@ enum hda_vendor_status_dspio {
VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
};
-/*
- * Chip Io Status codes
- */
enum hda_vendor_status_chipio {
/* Success */
VENDOR_STATUS_CHIPIO_OK = 0x00,
@@ -199,9 +651,6 @@ enum hda_vendor_status_chipio {
VENDOR_STATUS_CHIPIO_BUSY = 0x01
};
-/*
- * CA0132 sample rate
- */
enum ca0132_sample_rate {
SR_6_000 = 0x00,
SR_8_000 = 0x01,
@@ -225,112 +674,115 @@ enum ca0132_sample_rate {
SR_RATE_UNKNOWN = 0x1F
};
-/*
- * Scp Helper function
- */
-enum get_set {
- IS_SET = 0,
- IS_GET = 1,
+enum dsp_download_state {
+ DSP_DOWNLOAD_FAILED = -1,
+ DSP_DOWNLOAD_INIT = 0,
+ DSP_DOWNLOADING = 1,
+ DSP_DOWNLOADED = 2
};
-/*
- * Duplicated from ca0110 codec
- */
+struct hda_stream_format {
+ unsigned int sample_rate;
+ unsigned short valid_bits_per_sample;
+ unsigned short container_size;
+ unsigned short number_channels;
+};
-static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
-{
- if (pin) {
- snd_hda_set_pin_ctl(codec, pin, PIN_HP);
- if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- }
- if (dac)
- snd_hda_codec_write(codec, dac, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
-}
+/* retrieve parameters from hda format */
+#define get_hdafmt_chs(fmt) (fmt & 0xf)
+#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
+#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
+#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)
-static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
-{
- if (pin) {
- snd_hda_set_pin_ctl(codec, pin, PIN_IN |
- snd_hda_get_default_vref(codec, pin));
- if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- }
- if (adc)
- snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
-}
+struct ca0132_spec {
+ struct snd_kcontrol_new *mixers[5];
+ 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 */
+ struct auto_pin_cfg autocfg;
-static char *dirstr[2] = { "Playback", "Capture" };
+ /* Nodes configurations */
+ struct hda_multi_out multiout;
+ hda_nid_t out_pins[5];
+ hda_nid_t dacs[5];
+ unsigned int num_outputs;
+ hda_nid_t input_pins[5];
+ hda_nid_t adcs[5];
+ unsigned int num_inputs;
+ hda_nid_t shared_mic_nid;
+ hda_nid_t shared_out_nid;
+ struct hda_pcm pcm_rec[5]; /* PCM information */
+
+ /* chip access */
+ struct mutex chipio_mutex; /* chip access mutex */
+ u32 curr_chip_addx;
+
+ /* DSP download related */
+ enum dsp_download_state dsp_state;
+ unsigned int dsp_stream_id;
+ unsigned int wait_scp;
+ unsigned int wait_scp_header;
+ unsigned int wait_num_data;
+ unsigned int scp_resp_header;
+ unsigned int scp_resp_data[4];
+ unsigned int scp_resp_count;
+
+ /* mixer and effects related */
+ unsigned char dmic_ctl;
+ int cur_out_type;
+ int cur_mic_type;
+ long vnode_lvol[VNODES_COUNT];
+ long vnode_rvol[VNODES_COUNT];
+ long vnode_lswitch[VNODES_COUNT];
+ long vnode_rswitch[VNODES_COUNT];
+ long effects_switch[EFFECTS_COUNT];
+ long voicefx_val;
+ long cur_mic_boost;
+
+ #ifdef ENABLE_TUNING_CONTROLS
+ long cur_ctl_vals[TUNING_CTLS_COUNT];
+ #endif
+};
-static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
+/*
+ * CA0132 codec access
+ */
+unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm, unsigned int *res)
{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ unsigned int response;
+ response = snd_hda_codec_read(codec, nid, 0, verb, parm);
+ *res = response;
+
+ return ((response == -1) ? -1 : 0);
}
-static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
+static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
+ unsigned short converter_format, unsigned int *res)
{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
+ converter_format & 0xffff, res);
}
-#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
-#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
-#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
-#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
-#define add_mono_switch(codec, nid, pfx, chan) \
- _add_switch(codec, nid, pfx, chan, 0)
-#define add_mono_volume(codec, nid, pfx, chan) \
- _add_volume(codec, nid, pfx, chan, 0)
-#define add_in_mono_switch(codec, nid, pfx, chan) \
- _add_switch(codec, nid, pfx, chan, 1)
-#define add_in_mono_volume(codec, nid, pfx, chan) \
- _add_volume(codec, nid, pfx, chan, 1)
+static int codec_set_converter_stream_channel(struct hda_codec *codec,
+ hda_nid_t nid, unsigned char stream,
+ unsigned char channel, unsigned int *res)
+{
+ unsigned char converter_stream_channel = 0;
+ converter_stream_channel = (stream << 4) | (channel & 0x0f);
+ return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
+ converter_stream_channel, res);
+}
/*
- * CA0132 specific
+ * CA0132 chip access stuffs
*/
-
-struct ca0132_spec {
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
- hda_nid_t hp_dac;
- hda_nid_t input_pins[AUTO_PIN_LAST];
- hda_nid_t adcs[AUTO_PIN_LAST];
- hda_nid_t dig_out;
- hda_nid_t dig_in;
- unsigned int num_inputs;
- long curr_hp_switch;
- long curr_hp_volume[2];
- long curr_speaker_switch;
- struct mutex chipio_mutex;
- const char *input_labels[AUTO_PIN_LAST];
- struct hda_pcm pcm_rec[2]; /* PCM information */
-};
-
-/* Chip access helper function */
static int chipio_send(struct hda_codec *codec,
unsigned int reg,
- unsigned int data)
+ u32 data)
{
unsigned int res;
int retry = 50;
@@ -342,284 +794,3098 @@ static int chipio_send(struct hda_codec *codec,
if (res == VENDOR_STATUS_CHIPIO_OK)
return 0;
} while (--retry);
- return -EIO;
+
+ CTASSERT(0);
+ return -1;
}
-/*
- * Write chip address through the vendor widget -- NOT protected by the Mutex!
- */
-static int chipio_write_address(struct hda_codec *codec,
- unsigned int chip_addx)
+static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
{
- int res;
+ struct ca0132_spec *spec = codec->spec;
+ int status = 0;
+
+ if (spec->curr_chip_addx == chip_addx)
+ return 0;
/* send low 16 bits of the address */
- res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
- chip_addx & 0xffff);
+ status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+ chip_addx & 0xffff);
- if (res != -EIO) {
+ if (SUCCEEDED(status)) {
/* send high 16 bits of the address */
- res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
- chip_addx >> 16);
+ status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+ chip_addx >> 16);
}
- return res;
-}
+ CTASSERT(SUCCEEDED(status));
+ spec->curr_chip_addx = SUCCEEDED(status) ? chip_addx : ~0UL;
-/*
- * Write data through the vendor widget -- NOT protected by the Mutex!
- */
+ return status;
+}
-static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+static int chipio_write_data(struct hda_codec *codec, u32 data)
{
- int res;
+ struct ca0132_spec *spec = codec->spec;
+ int status = 0;
/* send low 16 bits of the data */
- res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+ status = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
- if (res != -EIO) {
+ if (SUCCEEDED(status)) {
/* send high 16 bits of the data */
- res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
- data >> 16);
+ status = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+ data >> 16);
}
- return res;
+ CTASSERT(SUCCEEDED(status));
+ spec->curr_chip_addx = SUCCEEDED(status) ?
+ (spec->curr_chip_addx + 4) : ~0UL;
+
+ return status;
+}
+
+static int chipio_write_data_multiple(struct hda_codec *codec,
+ const u32 *data,
+ unsigned int count)
+{
+ int status = 0;
+
+ if (data == NULL)
+ return FAIL_MSG(-1, "chipio_write_data null ptr");
+
+ while ((count-- != 0) && (status == 0))
+ status = chipio_write_data(codec, *data++);
+
+ return status;
}
-/*
- * Read data through the vendor widget -- NOT protected by the Mutex!
- */
static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
{
- int res;
+ struct ca0132_spec *spec = codec->spec;
+ int status = 0;
/* post read */
- res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+ status = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
- if (res != -EIO) {
+ if (SUCCEEDED(status)) {
/* read status */
- res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
}
- if (res != -EIO) {
+ if (SUCCEEDED(status)) {
/* read data */
*data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_HIC_READ_DATA,
- 0);
+ VENDOR_CHIPIO_HIC_READ_DATA, 0);
}
- return res;
+ CTASSERT(SUCCEEDED(status));
+ spec->curr_chip_addx = SUCCEEDED(status) ?
+ (spec->curr_chip_addx + 4) : ~0UL;
+
+ return status;
}
-/*
- * Write given value to the given address through the chip I/O widget.
- * protected by the Mutex
- */
-static int chipio_write(struct hda_codec *codec,
- unsigned int chip_addx, const unsigned int data)
+static int chipio_write(struct hda_codec *codec, u32 chip_addx,
+ const u32 data)
{
struct ca0132_spec *spec = codec->spec;
- int err;
+ int status;
mutex_lock(&spec->chipio_mutex);
-
- /* write the address, and if successful proceed to write data */
- err = chipio_write_address(codec, chip_addx);
- if (err < 0)
- goto exit;
-
- err = chipio_write_data(codec, data);
- if (err < 0)
- goto exit;
-
-exit:
+ status = chipio_write_addx(codec, chip_addx);
+ if (SUCCEEDED(status))
+ status = chipio_write_data(codec, data);
mutex_unlock(&spec->chipio_mutex);
- return err;
+
+ return status;
}
-/*
- * Read the given address through the chip I/O widget
- * protected by the Mutex
- */
-static int chipio_read(struct hda_codec *codec,
- unsigned int chip_addx, unsigned int *data)
+static int chipio_write_multiple(struct hda_codec *codec,
+ u32 chip_addx,
+ const u32 *data,
+ unsigned int count)
{
struct ca0132_spec *spec = codec->spec;
- int err;
+ int status;
mutex_lock(&spec->chipio_mutex);
+ status = chipio_write_addx(codec, chip_addx);
+ if (SUCCEEDED(status))
+ status = chipio_write_data_multiple(codec, data, count);
+ mutex_unlock(&spec->chipio_mutex);
- /* write the address, and if successful proceed to write data */
- err = chipio_write_address(codec, chip_addx);
- if (err < 0)
- goto exit;
+ return status;
+}
- err = chipio_read_data(codec, data);
- if (err < 0)
- goto exit;
+static int chipio_read(struct hda_codec *codec, u32 chip_addx,
+ unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
-exit:
+ mutex_lock(&spec->chipio_mutex);
+ status = chipio_write_addx(codec, chip_addx);
+ if (SUCCEEDED(status))
+ status = chipio_read_data(codec, data);
mutex_unlock(&spec->chipio_mutex);
- return err;
+
+ return status;
}
-/*
- * PCM stuffs
- */
-static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
+static void chipio_set_control_flag(struct hda_codec *codec,
+ enum control_flag_id flag_id,
+ bool flag_state)
{
- unsigned int oldval, newval;
+ unsigned int val;
+ unsigned int flag_bit;
- if (!nid)
- return;
+ flag_bit = (flag_state ? 1 : 0);
+ val = (flag_bit << 7) | (flag_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_FLAG_SET, val);
+}
- snd_printdd("ca0132_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
+static void chipio_set_control_param(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int val;
- /* update the format-id if changed */
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT,
- 0);
- if (oldval != format) {
- msleep(20);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ mutex_lock(&spec->chipio_mutex);
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ } else
+ CA0132_LOG("set_control_param:FAIL! id=0x%x, val=%d\n",
+ param_id, param_val);
+ mutex_unlock(&spec->chipio_mutex);
}
+}
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- }
+static void chipio_set_conn_rate(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
+ rate);
}
-static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+static void chipio_enable_clocks(struct hda_codec *codec)
{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
+ mutex_unlock(&spec->chipio_mutex);
}
+
/*
- * PCM callbacks
+ * CA0132 DSP IO stuffs
*/
-static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int dspio_send(struct hda_codec *codec, unsigned int reg,
+ unsigned int data)
{
- struct ca0132_spec *spec = codec->spec;
+ unsigned int res;
+ int retry = 50;
- ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+ /* send bits of data specified by reg to dsp */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
+ if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
+ return res;
+ } while (--retry);
- return 0;
+ CTASSERT(retry);
+ return -1;
}
-static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static void dspio_write_wait(struct hda_codec *codec)
{
- struct ca0132_spec *spec = codec->spec;
+ int cur_val, prv_val;
+ int retry = 50;
+
+ cur_val = 0;
+ do {
+ prv_val = cur_val;
+ msleep(20);
+ dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1);
+ dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
+ cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_SCP_READ_COUNT, 0);
+ } while (cur_val && (cur_val == prv_val) && --retry);
+}
+
+static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ dspio_write_wait(codec);
+
+ mutex_lock(&spec->chipio_mutex);
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
+ scp_data & 0xffff);
+ if (SUCCEEDED(status))
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
+ scp_data >> 16);
+ mutex_unlock(&spec->chipio_mutex);
+
+ /* OK, now check if the write itself has executed*/
+ if (SUCCEEDED(status))
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+
+ if (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL)
+ CA0132_LOG("dspio_write: SCP_COMMAND_QUEUE_FULL!!!\n\n");
+
+ return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
+ -1 : 0;
+}
+
+static int dspio_write_multiple(struct hda_codec *codec,
+ unsigned int *buffer, unsigned int size)
+{
+ int status = 0;
+ unsigned int count;
+
+ if ((buffer == NULL))
+ return -1;
+
+ count = 0;
+ while (count < size) {
+ status = dspio_write(codec, *buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ return status;
+}
+
+static int dspio_read(struct hda_codec *codec, unsigned int *data)
+{
+ int status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0);
+ if (SUCCEEDED(status))
+ status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
+
+ if (FAILED(status) ||
+ (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
+ return -1;
+
+ *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_SCP_READ_DATA, 0);
+
+ return 0;
+}
+
+static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
+ unsigned int *buf_size, unsigned int size_count)
+{
+ int status = 0;
+ unsigned int size = *buf_size;
+ unsigned int count;
+ unsigned int skip_count;
+ unsigned int dummy;
+
+ if ((buffer == NULL))
+ return -1;
+
+ count = 0;
+ while (count < size && count < size_count) {
+ status = dspio_read(codec, buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ skip_count = count;
+ if (SUCCEEDED(status)) {
+ while (skip_count < size) {
+ status = dspio_read(codec, &dummy);
+ if (status != 0)
+ break;
+ skip_count++;
+ }
+ }
+ *buf_size = count;
+
+ return status;
+}
+
+static inline unsigned int
+make_scp_header(unsigned int target_id, unsigned int source_id,
+ unsigned int get_flag, unsigned int req,
+ unsigned int device_flag, unsigned int resp_flag,
+ unsigned int error_flag, unsigned int data_size)
+{
+ unsigned int header = 0;
+
+ header = (data_size & 0x1f) << 27;
+ header |= (error_flag & 0x01) << 26;
+ header |= (resp_flag & 0x01) << 25;
+ header |= (device_flag & 0x01) << 24;
+ header |= (req & 0x7f) << 17;
+ header |= (get_flag & 0x01) << 16;
+ header |= (source_id & 0xff) << 8;
+ header |= target_id & 0xff;
+
+ return header;
+}
+
+static inline void
+extract_scp_header(unsigned int header,
+ unsigned int *target_id, unsigned int *source_id,
+ unsigned int *get_flag, unsigned int *req,
+ unsigned int *device_flag, unsigned int *resp_flag,
+ unsigned int *error_flag, unsigned int *data_size)
+{
+ if (data_size)
+ *data_size = (header >> 27) & 0x1f;
+ if (error_flag)
+ *error_flag = (header >> 26) & 0x01;
+ if (resp_flag)
+ *resp_flag = (header >> 25) & 0x01;
+ if (device_flag)
+ *device_flag = (header >> 24) & 0x01;
+ if (req)
+ *req = (header >> 17) & 0x7f;
+ if (get_flag)
+ *get_flag = (header >> 16) & 0x01;
+ if (source_id)
+ *source_id = (header >> 8) & 0xff;
+ if (target_id)
+ *target_id = header & 0xff;
+}
+
+#define SCP_MAX_DATA_WORDS (16)
+
+/* Structure to contain any SCP message */
+struct scp_msg {
+ unsigned int hdr;
+ unsigned int data[SCP_MAX_DATA_WORDS];
+};
+
+static void dspio_clear_response_queue(struct hda_codec *codec)
+{
+ unsigned int dummy = 0;
+ int status = -1;
+
+ /* clear all from the response queue */
+ do {
+ status = dspio_read(codec, &dummy);
+ } while (status == 0);
+}
+
+static int dspio_get_response_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int data = 0;
+ unsigned int count;
+
+ if (FAILED(dspio_read(codec, &data)))
+ return -1;
+
+ CA0132_LOG("dspio_get_response_data: 0x%08x\n", data);
+ if ((data & 0x00ffffff) == spec->wait_scp_header) {
+ spec->scp_resp_header = data;
+ spec->scp_resp_count = data >> 27;
+ count = spec->wait_num_data;
+ dspio_read_multiple(codec, spec->scp_resp_data,
+ &spec->scp_resp_count, count);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int dspio_send_scp_message(struct hda_codec *codec,
+ unsigned char *send_buf,
+ unsigned int send_buf_size,
+ unsigned char *return_buf,
+ unsigned int return_buf_size,
+ unsigned int *bytes_returned)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int retry;
+ int status = -1;
+ unsigned int scp_send_size = 0;
+ unsigned int total_size;
+ bool waiting_for_resp = false;
+ unsigned int header;
+ struct scp_msg *ret_msg;
+ unsigned int resp_src_id, resp_target_id;
+ unsigned int data_size, src_id, target_id, get_flag, device_flag;
+
+ if (bytes_returned)
+ *bytes_returned = 0;
+
+ /* get scp header from buffer */
+ header = *((unsigned int *)send_buf);
+ extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
+ &device_flag, NULL, NULL, &data_size);
+ scp_send_size = data_size + 1;
+ total_size = (scp_send_size * 4);
+
+ CTASSERT(send_buf_size >= total_size);
+ if (send_buf_size < total_size)
+ return -1;
+
+ if (get_flag || device_flag) {
+ if (!return_buf || return_buf_size < 4 || !bytes_returned) {
+ CTASSERT(0);
+ return -1;
+ }
+ spec->wait_scp_header = *((unsigned int *)send_buf);
+
+ /* swap source id with target id */
+ resp_target_id = src_id;
+ resp_src_id = target_id;
+ spec->wait_scp_header &= 0xffff0000;
+ spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
+ spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
+ spec->wait_scp = 1;
+ waiting_for_resp = true;
+ }
+
+ status = dspio_write_multiple(codec, (unsigned int *)send_buf,
+ scp_send_size);
+ if (FAILED(status)) {
+ CTASSERT(0);
+ spec->wait_scp = 0;
+ return status;
+ }
+
+ if (waiting_for_resp) {
+ memset(return_buf, 0, return_buf_size);
+ retry = 50;
+ do {
+ msleep(20);
+ } while (spec->wait_scp && (--retry != 0));
+ waiting_for_resp = false;
+ if (retry != 0) {
+ ret_msg = (struct scp_msg *)return_buf;
+ memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
+ memcpy(&ret_msg->data, spec->scp_resp_data,
+ spec->wait_num_data);
+ *bytes_returned = (spec->scp_resp_count + 1) * 4;
+ status = 0;
+ } else {
+ CTASSERT(*bytes_returned == 0);
+ status = -1;
+ }
+ spec->wait_scp = 0;
+ }
+
+ return status;
+}
+
+static int dspio_scp(struct hda_codec *codec,
+ int mod_id, int req, int dir, void *data, unsigned int len,
+ void *reply, unsigned int *reply_len)
+{
+ int status = 0;
+ struct scp_msg scp_send, scp_reply;
+ unsigned int ret_bytes, send_size, ret_size;
+ unsigned int send_get_flag, reply_resp_flag, reply_error_flag;
+ unsigned int reply_data_size;
+
+ memset(&scp_send, 0, sizeof(scp_send));
+ memset(&scp_reply, 0, sizeof(scp_reply));
+
+ CTASSERT((len == 0 || data != NULL) && len <= SCP_MAX_DATA_WORDS);
+ if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS))
+ return -1;
+
+ CTASSERT(dir != SCP_GET || reply != NULL);
+ if (dir == SCP_GET && reply == NULL)
+ return FAIL_MSG(-1, "dspio_scp get but has no buffer");
+
+ CTASSERT((reply == NULL) || (reply_len != NULL && (*reply_len > 0)));
+ if (reply != NULL && (reply_len == NULL || (*reply_len == 0)))
+ return FAIL_MSG(-1, "dspio_scp bad resp buf len parms");
+
+ scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req,
+ 0, 0, 0, len/sizeof(unsigned int));
+ if (data != NULL && len > 0) {
+ len = min((unsigned int)(sizeof(scp_send.data)), len);
+ memcpy(scp_send.data, data, len);
+ }
+
+ ret_bytes = 0;
+ send_size = sizeof(unsigned int) + len;
+ status = dspio_send_scp_message(codec, (unsigned char *)&scp_send,
+ send_size, (unsigned char *)&scp_reply,
+ sizeof(scp_reply), &ret_bytes);
+
+/* for debugging */
+#ifdef ENABLE_CA0132_DEBUG
+{
+ unsigned int x, *p, i;
+ unsigned int source_id;
+ unsigned int target_id;
+ unsigned int req;
+ unsigned int get_flag;
+ unsigned int resp_flag;
+ unsigned int device_flag;
+ unsigned int error_flag;
+ unsigned int data_size;
+
+ memcpy(&x, &scp_send.hdr, sizeof(unsigned int));
+ extract_scp_header(x, &target_id, &source_id,
+ &get_flag, &req, &device_flag, &resp_flag,
+ &error_flag, &data_size);
+
+ snd_printdd(
+ "ScpDispatch ----- REQ: "
+ "HDR=0x%08x "
+ "SID=%04x "
+ "TID=%04x "
+ "Req=%04x "
+ "GRDE=%d %d %d %d "
+ "Siz=%d [ ",
+ x,
+ source_id,
+ target_id,
+ req,
+ get_flag,
+ resp_flag,
+ device_flag,
+ error_flag,
+ data_size);
+
+ for (x = 0; x < data_size; x++)
+ snd_printdd("0x%08x ", scp_send.data[x].ui);
+
+ snd_printdd("]\n");
+
+ if (get_flag) {
+ if (ret_bytes >= sizeof(scp_reply.hdr)) {
+ memcpy(&x, &scp_reply.hdr, sizeof(unsigned int));
+ extract_scp_header(x, &target_id, &source_id,
+ &get_flag, &req, &device_flag,
+ &resp_flag, &error_flag, &data_size);
+
+ snd_printdd(
+ " ----- REP: "
+ "HDR=0x%08x "
+ "SID=%04x "
+ "TID=%04x "
+ "Req=%04x "
+ "GRDE=%d %d %d %d "
+ "Siz=%d [ ",
+ x,
+ source_id,
+ target_id,
+ req,
+ get_flag,
+ resp_flag,
+ device_flag,
+ error_flag,
+ data_size);
+
+ for (x = 0; x < data_size; x++)
+ snd_printdd("0x%08x ", scp_reply.data[x].ui);
+ } else {
+ snd_printdd("REP: (too short,nbytes=%d) [", ret_bytes);
+
+ for (p = (unsigned int *)&scp_reply, i = 0;
+ i < ret_bytes; p++, i++) {
+ snd_printdd("0x%04x ", *p);
+ }
+ }
+ snd_printdd("]\n");
+ }
+}
+#endif
+
+ if (FAILED(status))
+ return FAIL_MSG(status, "dspio_scp: send scp msg failed");
+
+ /* extract send and reply headers members */
+ extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag,
+ NULL, NULL, NULL, NULL, NULL);
+ extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL,
+ &reply_resp_flag, &reply_error_flag,
+ &reply_data_size);
+
+ if (!send_get_flag)
+ return 0;
+
+ if (reply_resp_flag && !reply_error_flag) {
+ ret_size = (ret_bytes - sizeof(scp_reply.hdr))
+ / sizeof(unsigned int);
+
+ CTASSERT(dir == SCP_GET && reply != NULL && reply_len != NULL);
+
+ if (*reply_len < ret_size*sizeof(unsigned int)) {
+ status = FAIL_MSG(-1, "reply too long for buf");
+ } else if (ret_size != reply_data_size) {
+ status = FAIL_MSG(-1, "RetLen and HdrLen .NE.");
+ } else {
+ *reply_len = ret_size*sizeof(unsigned int);
+ memcpy(reply, scp_reply.data, *reply_len);
+ }
+ } else {
+ status = FAIL_MSG(-1, "reply ill-formed or errflag set");
+ }
+
+ return status;
+}
+
+static int dspio_set_param(struct hda_codec *codec, int mod_id,
+ int req, void *data, unsigned int len)
+{
+ return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
+}
+
+static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
+{
+ int status = 0;
+ unsigned int size = sizeof(dma_chan);
+
+ CA0132_DSP_LOG(" dspio_alloc_dma_chan() -- begin");
+ status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
+ SCP_GET, NULL, 0, dma_chan, &size);
+
+ if (FAILED(status)) {
+ CA0132_DSP_LOG("dspio_alloc_dma_chan: SCP Failed");
+ return -1;
+ }
+
+ if ((*dma_chan + 1) == 0) {
+ CA0132_DSP_LOG("no free dma channels to allocate");
+ return -1;
+ }
+
+ CA0132_LOG("dspio_alloc_dma_chan: chan=%d\n", *dma_chan);
+ CA0132_DSP_LOG(" dspio_alloc_dma_chan() -- complete");
+
+ return status;
+}
+
+static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
+{
+ int status = 0;
+ unsigned int dummy = 0;
+
+ CA0132_DSP_LOG(" dspio_free_dma_chan() -- begin");
+ CA0132_LOG("dspio_free_dma_chan: chan=%d\n", dma_chan);
+
+ status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
+ SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy);
+
+ if (FAILED(status)) {
+ CA0132_DSP_LOG("dspio_free_dma_chan: SCP Failed");
+ return -1;
+ }
+
+ CA0132_DSP_LOG(" dspio_free_dma_chan() -- complete");
+
+ return status;
+}
+
+/*
+ * CA0132 DSP access stuffs
+ */
+static int dsp_set_run_state(struct hda_codec *codec)
+{
+ unsigned int dbg_ctrl_reg;
+ unsigned int halt_state;
+ int err;
+
+ err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >>
+ DSP_DBGCNTL_STATE_LOBIT;
+
+ if (halt_state != 0) {
+ dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) &
+ DSP_DBGCNTL_SS_MASK);
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) &
+ DSP_DBGCNTL_EXEC_MASK;
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int dsp_reset(struct hda_codec *codec)
+{
+ unsigned int res;
+ int retry = 20;
+
+ CA0132_LOG("dsp_reset\n");
+ do {
+ res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0);
+ retry--;
+ } while (res == -1 && retry);
+
+ if (!retry) {
+ CA0132_LOG("dsp_reset timeout\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
+ bool *code, bool *yram)
+{
+ *code = *yram = false;
+
+ if (UC_RANGE(chip_addx, 1)) {
+ *code = true;
+ return UC_OFF(chip_addx);
+ } else if (X_RANGE_ALL(chip_addx, 1)) {
+ return X_OFF(chip_addx);
+ } else if (Y_RANGE_ALL(chip_addx, 1)) {
+ *yram = true;
+ return Y_OFF(chip_addx);
+ }
+
+ return (unsigned int)INVALID_CHIP_ADDRESS;
+}
+
+static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
+{
+ unsigned int dma_chnlstart_reg;
+
+ chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg);
+
+ return ((dma_chnlstart_reg & (1 <<
+ (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0);
+}
+
+static int dsp_dma_setup_common(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ unsigned int chnl_prop;
+ unsigned int dsp_addx;
+ unsigned int active;
+ bool code, yram;
+
+ CA0132_DSP_LOG("-- dsp_dma_setup_common() -- Begin ---------");
+
+ CTASSERT(dma_chan < DSPDMAC_DMA_CFG_CHANNEL_COUNT);
+ if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT)
+ return FAIL_MSG(-1, "dma chan num invalid");
+
+ if (dsp_is_dma_active(codec, dma_chan))
+ return FAIL_MSG(-1, "dma already active");
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+
+ if (dsp_addx == INVALID_CHIP_ADDRESS)
+ return FAIL_MSG(-1, "invalid chip addr");
+
+ chnl_prop = DSPDMAC_CHNLPROP_AC_MASK;
+ active = 0;
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() start reg pgm");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET,
+ &chnl_prop);
+
+ if (FAILED(status))
+ return FAIL_MSG(-1, "read CHNLPROP Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() Read CHNLPROP");
+ }
+
+ if (!code)
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+ else
+ chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan));
+
+ status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write CHNLPROP Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() Write CHNLPROP");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET,
+ &active);
+
+ if (FAILED(status))
+ return FAIL_MSG(-1, "read ACTIVE Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() Read ACTIVE");
+ }
+
+ active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) &
+ DSPDMAC_ACTIVE_AAR_MASK;
+
+ status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write ACTIVE Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() Write ACTIVE");
+
+ status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan),
+ port_map_mask);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write AUDCHSEL Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() Write AUDCHSEL");
+
+ status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan),
+ DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write IRQCNT Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup_common() Write IRQCNT");
+
+ CA0132_LOG(
+ "ChipA=0x%x,DspA=0x%x,dmaCh=%u, "
+ "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n",
+ chip_addx, dsp_addx, dma_chan,
+ port_map_mask, chnl_prop, active);
+
+ CA0132_DSP_LOG("-- dsp_dma_setup_common() -- Complete ---------");
+
+ return 0;
+}
+
+static int dsp_dma_setup(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int count,
+ unsigned int dma_chan)
+{
+ int status = 0;
+ bool code, yram;
+ unsigned int dsp_addx;
+ unsigned int addr_field;
+ unsigned int incr_field;
+ unsigned int base_cnt;
+ unsigned int cur_cnt;
+ unsigned int dma_cfg = 0;
+ unsigned int adr_ofs = 0;
+ unsigned int xfr_cnt = 0;
+ const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT -
+ DSPDMAC_XFRCNT_BCNT_LOBIT + 1);
+
+ CA0132_DSP_LOG("-- dsp_dma_setup() -- Begin ---------");
+
+ CTASSERT(count <= max_dma_count);
+ if (count > max_dma_count)
+ return FAIL_MSG(-1, "count too big");
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+ if (dsp_addx == INVALID_CHIP_ADDRESS)
+ return FAIL_MSG(-1, "invalid chip addr");
+
+ CA0132_DSP_LOG(" dsp_dma_setup() start reg pgm");
+
+ addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT;
+ incr_field = 0;
+
+ if (!code) {
+ addr_field <<= 1;
+ if (yram)
+ addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT);
+
+ incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT);
+ }
+
+ CTASSERT((addr_field & DSPDMAC_DMACFG_DBADR_MASK) == addr_field);
+ dma_cfg = addr_field + incr_field;
+ status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan),
+ dma_cfg);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write DMACFG Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup() Write DMACFG");
+
+ adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT +
+ (code ? 0 : 1));
+ CTASSERT((adr_ofs & DSPDMAC_DSPADROFS_BOFS_MASK) == adr_ofs);
+
+ status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan),
+ adr_ofs);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write DSPADROFS Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup() Write DSPADROFS");
+
+ base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT;
+ CTASSERT((base_cnt & DSPDMAC_XFRCNT_BCNT_MASK) == base_cnt);
+
+ cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT;
+ CTASSERT((cur_cnt & DSPDMAC_XFRCNT_CCNT_MASK) == cur_cnt);
+
+ xfr_cnt = base_cnt | cur_cnt;
+
+ status = chipio_write(codec,
+ DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write XFRCNT Reg fail");
+
+ CA0132_DSP_LOG(" dsp_dma_setup() Write XFRCNT");
+
+ CA0132_LOG(
+ "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, "
+ "ADROFS=0x%x, XFRCNT=0x%x\n",
+ chip_addx, count, dma_cfg, adr_ofs, xfr_cnt);
+
+ CA0132_DSP_LOG("-- dsp_dma_setup() -- Complete ---------");
+
+ return 0;
+}
+
+static int dsp_dma_start(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ CA0132_DSP_LOG("-- dsp_dma_start() -- Begin ---------");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, ®);
+
+ if (FAILED(status))
+ return FAIL_MSG(-1, "read CHNLSTART reg fail");
+
+ CA0132_DSP_LOG("-- dsp_dma_start() Read CHNLSTART");
+
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT)));
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write CHNLSTART reg fail");
+
+ CA0132_DSP_LOG("-- dsp_dma_start() -- Complete ---------");
+
+ return status;
+}
+
+static int dsp_dma_stop(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ CA0132_DSP_LOG("-- dsp_dma_stop() -- Begin ---------");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, ®);
+
+ if (FAILED(status))
+ return FAIL_MSG(-1, "read CHNLSTART reg fail");
+
+ CA0132_DSP_LOG("-- dsp_dma_stop() Read CHNLSTART");
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT)));
+ if (FAILED(status))
+ return FAIL_MSG(-1, "write CHNLSTART reg fail");
+
+ CA0132_DSP_LOG("-- dsp_dma_stop() -- Complete ---------");
+
+ return status;
+}
+
+static int dsp_allocate_router_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int ports_per_channel,
+ unsigned int start_device,
+ unsigned int *port_map)
+{
+ int status = 0;
+ int res;
+ u8 val;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (FAILED(status))
+ return status;
+
+ val = start_device << 6;
+ val |= (ports_per_channel - 1) << 4;
+ val |= num_chans - 1;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET,
+ val);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (FAILED(status))
+ return status;
+
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_GET, 0);
+
+ *port_map = res;
+
+ return (res < 0) ? -1 : 0;
+}
+
+static int dsp_free_router_ports(struct hda_codec *codec)
+{
+ int status = 0;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (FAILED(status))
+ return status;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_FREE_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+
+ return status;
+}
+
+static int dsp_allocate_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int rate_multi, unsigned int *port_map)
+{
+ int status = -1;
+
+ CA0132_DSP_LOG(" dsp_allocate_ports() -- begin");
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4))
+ return FAIL_MSG(status, "bad rate multiple");
+
+ status = dsp_allocate_router_ports(codec, num_chans,
+ rate_multi, 0, port_map);
+
+ CA0132_DSP_LOG(" dsp_allocate_ports() -- complete");
+
+ return status;
+}
+
+static int dsp_free_ports(struct hda_codec *codec)
+{
+ int status;
+
+ CA0132_DSP_LOG(" dsp_free_ports() -- begin");
+
+ status = dsp_free_router_ports(codec);
+ if (FAILED(status))
+ return FAIL_MSG(-1, "free router ports fail");
+
+ CA0132_DSP_LOG(" dsp_free_ports() -- complete");
+
+ return status;
+}
+
+static int dsp_allocate_ports_format(struct hda_codec *codec,
+ const unsigned short fmt,
+ unsigned int *port_map)
+{
+ int status = -1;
+ unsigned int num_chans;
+
+ unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1;
+ unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1;
+ unsigned int rate_multi = sample_rate_mul / sample_rate_div;
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4))
+ return FAIL_MSG(-1, "bad rate multiple");
+
+ num_chans = get_hdafmt_chs(fmt) + 1;
+
+ status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
+
+ return status;
+}
+
+/*
+ * HDA DMA engine stuffs for DSP code download
+ */
+struct dma_engine {
+ struct hda_codec *codec;
+ unsigned short m_converter_format;
+ void *m_buffer_addr;
+ unsigned int m_buffer_size;
+ unsigned int m_req_size;
+ struct snd_pcm_substream *substream;
+};
+
+enum dma_state {
+ DMA_STATE_RESET = 0,
+ DMA_STATE_STOP = 1,
+ DMA_STATE_RUN = 2
+};
+
+#define azx_pcm_open(a) (a->ops->open(a))
+#define azx_pcm_close(a) (a->ops->close(a))
+#define azx_pcm_prepare(a) (a->ops->prepare(a))
+#define azx_pcm_trigger(a, b) (a->ops->trigger(a, b))
+#define azx_pcm_hw_free(a) (a->ops->hw_free(a))
+
+static int dma_convert_to_hda_format(
+ struct hda_stream_format *stream_format,
+ unsigned short *hda_format)
+{
+ unsigned int format_val;
+
+ format_val = snd_hda_calc_stream_format(
+ stream_format->sample_rate,
+ stream_format->number_channels,
+ SNDRV_PCM_FORMAT_S32_LE,
+ stream_format->container_size, 0);
+
+ if (hda_format)
+ *hda_format = (unsigned short)format_val;
+
+ return 0;
+}
+
+static int dma_init(
+ struct hda_codec *codec,
+ struct dma_engine **pp_dma,
+ struct hda_stream_format *stream_format,
+ unsigned short *format,
+ unsigned int req_size)
+{
+ struct dma_engine *dma;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm *pcm;
+ struct snd_pcm_runtime *runtime;
+ unsigned int bits;
+ snd_pcm_uframes_t frames;
+
+ *pp_dma = NULL;
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+ memset((void *)dma, 0, sizeof(*dma));
+
+ dma_convert_to_hda_format(stream_format, format);
+ dma->m_converter_format = *format;
+
+ dma->substream = NULL;
+ pcm = codec->pcm_info->pcm;
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ substream; substream = substream->next) {
+ if (codec->pcm_info->pcm_type == HDA_PCM_TYPE_SPDIF)
+ continue;
+
+ if (!SUBSTREAM_BUSY(substream)) {
+ dma->substream = substream;
+ break;
+ }
+ }
+
+ if (NULL == dma->substream) {
+ kfree(dma);
+ return -1;
+ }
+
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ memset((void *)runtime, 0, sizeof(*runtime));
+ dma->substream->runtime = runtime;
+ dma->substream->private_data = pcm->private_data;
+
+ azx_pcm_open(dma->substream);
+ req_size = req_size * 2;
+ snd_pcm_lib_malloc_pages(dma->substream, req_size);
+
+ runtime->rate = stream_format->sample_rate;
+ runtime->channels = stream_format->number_channels;
+ runtime->format = SNDRV_PCM_FORMAT_S32_LE;
+ runtime->no_period_wakeup = 1;
+
+ bits = snd_pcm_format_physical_width(runtime->format);
+ runtime->sample_bits = bits;
+ bits *= runtime->channels;
+ runtime->frame_bits = bits;
+ frames = 1;
+ while (bits % 8 != 0) {
+ bits *= 2;
+ frames *= 2;
+ }
+ runtime->byte_align = bits / 8;
+ runtime->min_align = frames;
+ runtime->buffer_size = bytes_to_frames(runtime, req_size);
+ runtime->period_size = runtime->buffer_size;
+ dma->m_req_size = req_size;
+ dma->codec = codec;
+
+ *pp_dma = dma;
+ CA0132_LOG("dma_init: succeeded.\n");
+ return 0;
+}
+
+static int dma_prepare(struct dma_engine *dma)
+{
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ CA0132_LOG("dma_prepare: begin\n");
+ runtime = dma->substream->runtime;
+
+ err = azx_pcm_prepare(dma->substream);
+ if (err < 0)
+ return -1;
+
+ dma->m_buffer_size = snd_pcm_lib_buffer_bytes(dma->substream);
+ dma->m_buffer_addr = runtime->dma_area;
+
+ return 0;
+}
+
+static int dma_reset(struct dma_engine *dma)
+{
+ struct snd_pcm_runtime *runtime = dma->substream->runtime;
+
+ CA0132_LOG("dma_reset: begin\n");
+ azx_pcm_hw_free(dma->substream);
+ snd_pcm_lib_malloc_pages(dma->substream, dma->m_req_size);
+
+ azx_pcm_prepare(dma->substream);
+ dma->m_buffer_size = snd_pcm_lib_buffer_bytes(dma->substream);
+ dma->m_buffer_addr = runtime->dma_area;
+
+ return 0;
+}
+
+static int dma_set_state(struct dma_engine *dma, enum dma_state state)
+{
+ int cmd;
+
+ CA0132_LOG("dma_set_state state=%d\n", state);
+
+ switch (state) {
+ case DMA_STATE_RESET:
+ dma_reset(dma);
+ return 0;
+ case DMA_STATE_STOP:
+ cmd = SNDRV_PCM_TRIGGER_STOP;
+ break;
+ case DMA_STATE_RUN:
+ cmd = SNDRV_PCM_TRIGGER_START;
+ break;
+ default:
+ return 0;
+ }
+
+ azx_pcm_trigger(dma->substream, cmd);
+
+ return 0;
+}
+
+static unsigned int dma_get_buffer_size(struct dma_engine *dma)
+{
+ return dma->m_buffer_size;
+}
+
+static unsigned int *dma_get_buffer_addr(struct dma_engine *dma)
+{
+ return dma->m_buffer_addr;
+}
+
+static int dma_free_buffer(struct dma_engine *dma)
+{
+ azx_pcm_hw_free(dma->substream);
+ azx_pcm_close(dma->substream);
+ kfree(dma->substream->runtime);
+ dma->substream->runtime = NULL;
+ return 0;
+}
+
+static int dma_xfer(struct dma_engine *dma,
+ const unsigned int *data,
+ unsigned int count)
+{
+ memcpy(dma->m_buffer_addr, data, count);
+ return 0;
+}
+
+static void dma_get_converter_format(
+ struct dma_engine *dma,
+ unsigned short *format)
+{
+ if (format)
+ *format = dma->m_converter_format;
+}
+
+static unsigned int dma_get_stream_id(struct dma_engine *dma)
+{
+ struct ca0132_spec *spec = dma->codec->spec;
+
+ return spec->dsp_stream_id;
+}
+
+static int dma_exit(struct dma_engine *dma)
+{
+ CA0132_LOG("dma_exit\n");
+ kfree(dma);
+ return 0;
+}
+
+/*
+ * CA0132 chip DSP image segment stuffs
+ */
+struct dsp_image_seg {
+ u32 magic;
+ u32 chip_addr;
+ u32 count;
+ u32 data[0];
+};
+
+static const u32 g_magic_value = 0x4c46584d;
+static const u32 g_chip_addr_magic_value = 0xFFFFFF01;
+
+static bool is_valid(const struct dsp_image_seg *p)
+{
+ return p->magic == g_magic_value;
+}
+
+static bool is_hci_prog_list_seg(const struct dsp_image_seg *p)
+{
+ return g_chip_addr_magic_value == p->chip_addr;
+}
+
+static bool is_last(const struct dsp_image_seg *p)
+{
+ return p->count == 0;
+}
+
+static size_t dsp_sizeof(const struct dsp_image_seg *p)
+{
+ return sizeof(*p) + p->count*sizeof(u32);
+}
+
+static const struct dsp_image_seg *get_next_seg_ptr(
+ const struct dsp_image_seg *p)
+{
+ return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p));
+}
+
+/*
+ * CA0132 chip DSP trannsfer stuffs. For DSP download.
+ */
+#define INVALID_DMA_CHANNEL (~0UL)
+
+static int dspxfr_hci_write(struct hda_codec *codec,
+ const struct dsp_image_seg *fls)
+{
+ int status = 0;
+ const u32 *data;
+ unsigned int count;
+
+ CTASSERT(fls != NULL && fls->chip_addr == g_chip_addr_magic_value);
+ if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value)
+ return FAIL_MSG(-1, "hci_write invalid params");
+
+ count = fls->count;
+ data = (u32 *)(fls->data);
+ while (SUCCEEDED(status) && count >= 2) {
+ status = chipio_write(codec, data[0], data[1]);
+ if (FAILED(status))
+ status = FAIL_MSG(status, "hci_write chipio failed");
+ count -= 2;
+ data += 2;
+ }
+ return status;
+}
+
+static int dspxfr_one_seg(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ unsigned int reloc,
+ struct dma_engine *dma_engine,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ bool comm_dma_setup_done = false;
+ const unsigned int *data;
+ unsigned int chip_addx;
+ unsigned int words_to_write;
+ unsigned int buffer_size_words;
+ unsigned int *buffer_addx;
+ unsigned short hda_format;
+ unsigned int sample_rate_div;
+ unsigned int sample_rate_mul;
+ unsigned int num_chans;
+ unsigned int hda_frame_size_words;
+ unsigned int remainder_words;
+ const u32 *data_remainder;
+ u32 chip_addx_remainder;
+ unsigned int run_size_words;
+ const struct dsp_image_seg *hci_write = NULL;
+ int retry;
+
+ CTASSERT(fls != NULL);
+ if (fls == NULL)
+ return -1;
+ if (is_hci_prog_list_seg(fls)) {
+ hci_write = fls;
+ fls = get_next_seg_ptr(fls);
+ }
+
+ if (hci_write && (!fls || is_last(fls))) {
+ CA0132_LOG("hci_write\n");
+ return dspxfr_hci_write(codec, hci_write);
+ }
+
+ if (fls == NULL || dma_engine == NULL || port_map_mask == 0) {
+ CA0132_LOG("Invalid Params\n");
+ return -1;
+ }
+
+ data = fls->data;
+ chip_addx = fls->chip_addr,
+ words_to_write = fls->count;
+
+ if (!words_to_write)
+ return hci_write ? dspxfr_hci_write(codec, hci_write) : 0;
+ if (reloc)
+ chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2);
+
+ if (!UC_RANGE(chip_addx, words_to_write) &&
+ !X_RANGE_ALL(chip_addx, words_to_write) &&
+ !Y_RANGE_ALL(chip_addx, words_to_write)) {
+ CA0132_LOG("Invalid chip_addx Params\n");
+ return -1;
+ }
+
+ buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) /
+ sizeof(u32);
+
+ buffer_addx = dma_get_buffer_addr(dma_engine);
+
+ if (buffer_addx == NULL)
+ status = FAIL_MSG(-1, "dma_engine buffer NULL");
+
+ dma_get_converter_format(dma_engine, &hda_format);
+ sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1;
+ sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1;
+ num_chans = get_hdafmt_chs(hda_format) + 1;
+
+ hda_frame_size_words = ((sample_rate_div == 0) ? 0 :
+ (num_chans * sample_rate_mul / sample_rate_div));
+
+ buffer_size_words = min(buffer_size_words,
+ (unsigned int)(UC_RANGE(chip_addx, 1) ?
+ 65536 : 32768));
+ buffer_size_words -= buffer_size_words % hda_frame_size_words;
+ CA0132_LOG(
+ "chpadr=0x%08x frmsz=%u nchan=%u "
+ "rate_mul=%u div=%u bufsz=%u\n",
+ chip_addx, hda_frame_size_words, num_chans,
+ sample_rate_mul, sample_rate_div, buffer_size_words);
+
+ CTASSERT(SUCCEEDED(status));
+ CTASSERT(buffer_addx != NULL);
+ CTASSERT(buffer_size_words >= hda_frame_size_words);
+ CTASSERT(hda_frame_size_words > 0);
+
+ if ((buffer_addx == NULL) || (hda_frame_size_words == 0) ||
+ (buffer_size_words < hda_frame_size_words)) {
+ status = FAIL_MSG(-1, "dspxfr_one_seg:failed");
+ }
+
+ if (FAILED(status))
+ return status;
+
+ remainder_words = words_to_write % hda_frame_size_words;
+ data_remainder = data;
+ chip_addx_remainder = chip_addx;
+
+ data += remainder_words;
+ chip_addx += remainder_words*sizeof(u32);
+ words_to_write -= remainder_words;
+
+ while ((words_to_write != 0) && SUCCEEDED(status)) {
+ run_size_words = min(buffer_size_words, words_to_write);
+ CA0132_LOG("dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n",
+ words_to_write, run_size_words, remainder_words);
+ dma_set_state(dma_engine, DMA_STATE_STOP);
+ dma_xfer(dma_engine, data, run_size_words*sizeof(u32));
+ if (!comm_dma_setup_done && SUCCEEDED(status)) {
+ status = dsp_dma_stop(codec, dma_chan, ovly);
+ if (SUCCEEDED(status))
+ status = dsp_dma_setup_common(codec, chip_addx,
+ dma_chan, port_map_mask, ovly);
+ comm_dma_setup_done = true;
+ }
+
+ if (SUCCEEDED(status))
+ status = dsp_dma_setup(codec, chip_addx,
+ run_size_words, dma_chan);
+ if (SUCCEEDED(status))
+ status = dsp_dma_start(codec, dma_chan, ovly);
+ if (SUCCEEDED(status) && !dsp_is_dma_active(codec, dma_chan))
+ status = FAIL_MSG(-1, "dspxfr:DMA did not start");
+ if (SUCCEEDED(status))
+ status = dma_set_state(dma_engine, DMA_STATE_RUN);
+ if (SUCCEEDED(status)) {
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec,
+ chip_addx_remainder,
+ data_remainder,
+ remainder_words);
+ remainder_words = 0;
+ }
+ if (hci_write) {
+ status = dspxfr_hci_write(codec, hci_write);
+ hci_write = NULL;
+ }
+ retry = 5000;
+ while (dsp_is_dma_active(codec, dma_chan)) {
+ if (--retry <= 0)
+ break;
+ }
+ CA0132_DSP_LOG("+++++ DMA complete");
+ dma_set_state(dma_engine, DMA_STATE_STOP);
+ dma_set_state(dma_engine, DMA_STATE_RESET);
+ }
+
+ CTASSERT(run_size_words <= words_to_write);
+ data += run_size_words;
+ chip_addx += run_size_words*sizeof(u32);
+ words_to_write -= run_size_words;
+ }
+
+ if (SUCCEEDED(status) && (remainder_words != 0)) {
+ status = chipio_write_multiple(codec, chip_addx_remainder,
+ data_remainder, remainder_words);
+ }
+
+ return status;
+}
+
+static int dspxfr_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls_data,
+ unsigned int reloc, struct hda_stream_format *format,
+ bool ovly)
+{
+ int status = 0;
+ unsigned short hda_format = 0;
+ unsigned int response;
+ unsigned char stream_id = 0;
+ struct dma_engine *dma_engine;
+ unsigned int dma_chan;
+ unsigned int port_map_mask;
+ unsigned int buf_size;
+
+ CTASSERT(fls_data != NULL);
+ if (fls_data == NULL)
+ return -1;
+
+ buf_size = ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
+ status = dma_init(codec, &dma_engine, format, &hda_format, buf_size);
+
+ if (FAILED(status))
+ return -1;
+
+ dma_chan = 0;
+ do {
+ status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL,
+ hda_format, &response);
+ if (FAILED(status)) {
+ status = FAIL_MSG(status, "set converter format fail");
+ break;
+ }
+
+ status = dma_prepare(dma_engine);
+ if (FAILED(status))
+ break;
+
+ if (ovly) {
+ status = dspio_alloc_dma_chan(codec, &dma_chan);
+ if (FAILED(status)) {
+ status = FAIL_MSG(status, "alloc dmachan fail");
+ dma_chan = (unsigned int)INVALID_DMA_CHANNEL;
+ break;
+ }
+ }
+
+ port_map_mask = 0;
+ status = dsp_allocate_ports_format(codec, hda_format,
+ &port_map_mask);
+ if (FAILED(status)) {
+ status = FAIL_MSG(status, "alloc parts fail");
+ break;
+ }
+
+ stream_id = dma_get_stream_id(dma_engine);
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, stream_id, 0, &response);
+ if (FAILED(status)) {
+ status = FAIL_MSG(status, "set stream chan fail");
+ break;
+ }
+
+ while (SUCCEEDED(status) && (fls_data != NULL) &&
+ !is_last(fls_data)) {
+ if (!is_valid(fls_data)) {
+ status = FAIL_MSG(-1, "FLS check fail");
+ break;
+ }
+ status = dspxfr_one_seg(codec, fls_data, reloc,
+ dma_engine, dma_chan,
+ port_map_mask, ovly);
+ if (is_hci_prog_list_seg(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+
+ CTASSERT(fls_data != NULL);
+ if ((fls_data != NULL) && !is_last(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+
+ CTASSERT(fls_data != NULL);
+ }
+
+ if (port_map_mask != 0)
+ status = dsp_free_ports(codec);
+
+ if (FAILED(status))
+ break;
+
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, 0, 0, &response);
+ } while (0);
+
+ if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
+ status = dspio_free_dma_chan(codec, dma_chan);
+
+ status = dma_set_state(dma_engine, DMA_STATE_RESET);
+ if (FAILED(status))
+ status = FAIL_MSG(status, "dma set state Reset fail");
+
+ status = dma_free_buffer(dma_engine);
+ if (FAILED(status))
+ status = FAIL_MSG(status, "dma free buffer fail");
+
+ dma_exit(dma_engine);
+
+ return status;
+}
+
+/*
+ * CA0132 DSP download stuffs.
+ */
+static void dspload_post_setup(struct hda_codec *codec)
+{
+ CA0132_DSP_LOG("---- dspload_post_setup ------");
+
+ /*set DSP speaker to 2.0 configuration*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
+
+ /*update write pointer*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+}
+
+static int dspload_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ bool ovly,
+ unsigned int reloc,
+ bool autostart,
+ int router_chans)
+{
+ int status = 0;
+ struct hda_stream_format stream_format;
+
+ CA0132_DSP_LOG("---- dspload_image begin ------");
+ if (router_chans == 0) {
+ if (!ovly)
+ router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS;
+ else
+ router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS;
+ }
+
+ stream_format.sample_rate = 48000;
+ stream_format.number_channels = (unsigned short)router_chans;
+
+ while (stream_format.number_channels > 16) {
+ stream_format.sample_rate *= 2;
+ stream_format.number_channels /= 2;
+ }
+
+ stream_format.container_size = 32;
+ stream_format.valid_bits_per_sample = 32;
+
+ do {
+ CA0132_DSP_LOG("Ready to program DMA");
+ if (!ovly)
+ status = dsp_reset(codec);
+
+ if (FAILED(status))
+ break;
+
+ CA0132_DSP_LOG("dsp_reset() complete");
+ status = dspxfr_image(codec, fls, reloc, &stream_format, ovly);
+
+ if (FAILED(status))
+ break;
+
+ CA0132_DSP_LOG("dspxfr_image() complete");
+ if (autostart && !ovly) {
+ dspload_post_setup(codec);
+ status = dsp_set_run_state(codec);
+ }
+
+ CA0132_DSP_LOG("LOAD FINISHED");
+ } while (0);
+
+ return status;
+}
+
+static int dspload_get_speakereq_addx(struct hda_codec *codec,
+ unsigned int *x,
+ unsigned int *y)
+{
+ int status = 0;
+ struct { unsigned short x, y; } speakereq_info;
+ unsigned int size = sizeof(speakereq_info);
+
+ CA0132_DSP_LOG("dspload_get_speakereq_addx() -- begin");
+ status = dspio_scp(codec, MASTERCONTROL,
+ MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS,
+ SCP_GET, NULL, 0, &speakereq_info, &size);
+
+ if (FAILED(status)) {
+ CA0132_DSP_LOG("dspload_get_speakereq_addx: SCP Failed");
+ return -1;
+ }
+
+ *x = speakereq_info.x;
+ *y = speakereq_info.y;
+ CA0132_LOG("dspload_get_speakereq_addx: X=0x%x Y=0x%x\n", *x, *y);
+
+ CA0132_DSP_LOG("dspload_get_speakereq_addx() -- complete");
+
+ return status;
+}
+
+static int dspload_speakereq(struct hda_codec *codec)
+{
+ int status = 0;
+ const struct dsp_image_seg *image_x, *image_y;
+ const struct firmware *fw_entry;
+ unsigned int x, y;
+
+ CA0132_DSP_LOG("dspload_speakereq() -- begin");
+
+ if (request_firmware(&fw_entry, SPEQ_FILE, codec->bus->card->dev) != 0)
+ return -1;
+
+ if (fw_entry->size != SPEQ_SIZE) {
+ status = -1;
+ goto done_release_firmware;
+ }
+
+ image_x = (struct dsp_image_seg *)(fw_entry->data + 0x10);
+ image_y = (struct dsp_image_seg *)(fw_entry->data + 0x1028);
+
+ status = dspload_get_speakereq_addx(codec, &x, &y);
+ if (FAILED(status))
+ goto done_release_firmware;
+
+ status = dspload_image(codec, image_x, 1, x, 0, 8);
+ if (FAILED(status))
+ goto done_release_firmware;
+
+ status = dspload_image(codec, image_y, 1, y, 0, 8);
+
+done_release_firmware:
+ release_firmware(fw_entry);
+
+ CA0132_DSP_LOG("dspload_speakereq() -- complete");
+
+ return status;
+}
+
+static bool dspload_is_loaded(struct hda_codec *codec)
+{
+ unsigned int data = 0;
+ int status = 0;
+
+ status = chipio_read(codec, 0x40004, &data);
+ if (FAILED(status) || (data != 1))
+ return false;
+
+ return true;
+}
+
+static bool dspload_wait_loaded(struct hda_codec *codec)
+{
+ int retry = 100;
+
+ do {
+ msleep(20);
+ if (dspload_is_loaded(codec)) {
+ pr_info("DOWNLOAD OK :-) DSP IS RUNNING.\n");
+ return true;
+ }
+ } while (--retry);
+
+ pr_err("DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
+ return false;
+}
+
+/*
+ * PCM stuffs.
+ */
+static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag,
+ int channel_id, int format)
+{
+ unsigned int oldval, newval;
+
+ if (!nid)
+ return;
+
+ CA0132_LOG(
+ "ca0132_setup_stream: NID=0x%x, stream=0x%x, "
+ "channel=%d, format=0x%x\n",
+ nid, stream_tag, channel_id, format);
+
+ /* update the format-id if changed */
+ oldval = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ if (oldval != format) {
+ msleep(20);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ }
+
+ oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ newval = (stream_tag << 4) | channel_id;
+ if (oldval != newval) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ newval);
+ }
+}
+
+static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val;
+
+ if (!nid)
+ return;
+
+ CA0132_LOG("ca0132_cleanup_stream: NID=0x%x\n", nid);
+
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ if (!val)
+ return;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+}
+
+/*
+ * PCM playbacks
+ */
+
+static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING) {
+ spec->dsp_stream_id = stream_tag;
+ return 0;
+ }
+
+ ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+ return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ /*Allow stream some time to flush effects tail*/
+ msleep(50);
ca0132_cleanup_stream(codec, spec->dacs[0]);
+ return 0;
+}
+
+/*
+ * PCM capture
+ */
+
+static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ ca0132_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ ca0132_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+
+/*
+ * Controls stuffs.
+ */
+
+/*
+ * Mixer controls helpers.
+ */
+#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = ca0132_volume_info, \
+ .get = ca0132_volume_get, \
+ .put = ca0132_volume_put, \
+ .tlv = { .c = ca0132_volume_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = ca0132_switch_get, \
+ .put = ca0132_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+/* stereo */
+#define CA0132_CODEC_VOL(xname, nid, dir) \
+ CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_CODEC_MUTE(xname, nid, dir) \
+ CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
+
+
+/* The followings are for tuning of products */
+#ifdef ENABLE_TUNING_CONTROLS
+
+static unsigned int voice_focus_vals_lookup[] = {
+0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000,
+0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000,
+0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000,
+0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000,
+0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000,
+0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000,
+0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000,
+0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000,
+0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000,
+0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000,
+0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000,
+0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000,
+0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000,
+0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000,
+0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000,
+0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000,
+0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000,
+0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000,
+0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000,
+0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000,
+0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000,
+0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000,
+0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000,
+0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000,
+0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000,
+0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000,
+0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000
+};
+
+static unsigned int mic_svm_vals_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+static unsigned int equalizer_vals_lookup[] = {
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000,
+0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000,
+0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000,
+0x41C00000
+};
+
+static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int *lookup, int idx)
+{
+ int i = 0;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++)
+ if (nid == ca0132_tuning_ctls[i].nid)
+ break;
+
+ snd_hda_power_up(codec);
+ dspio_set_param(codec, ca0132_tuning_ctls[i].mid,
+ ca0132_tuning_ctls[i].req,
+ &(lookup[idx]), sizeof(unsigned int));
+ snd_hda_power_down(codec);
+
+ return 1;
+}
+
+static int tuning_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - TUNING_CTL_START_NID;
+
+ *valp = spec->cur_ctl_vals[idx];
+ return 0;
+}
+
+static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 20;
+ uinfo->value.integer.max = 180;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp - 20;
+ tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx);
+
+ return 1;
+}
+
+static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx);
+
+ return 0;
+}
+
+static int equalizer_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 48;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx);
+
+ return 1;
+}
+
+DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
+DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0);
+
+static int add_tuning_control(struct hda_codec *codec,
+ hda_nid_t pnid, hda_nid_t nid,
+ const char *name, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ knew.tlv.c = 0;
+ knew.tlv.p = 0;
+ switch (pnid) {
+ case VOICE_FOCUS:
+ knew.info = voice_focus_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = voice_focus_ctl_put;
+ knew.tlv.p = voice_focus_db_scale;
+ break;
+ case MIC_SVM:
+ knew.info = mic_svm_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = mic_svm_ctl_put;
+ break;
+ case EQUALIZER:
+ knew.info = equalizer_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = equalizer_ctl_put;
+ knew.tlv.p = eq_db_scale;
+ break;
+ default:
+ return 0;
+ }
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ sprintf(namestr, "%s %s Volume", name, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_tuning_ctls(struct hda_codec *codec)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++) {
+ err = add_tuning_control(codec,
+ ca0132_tuning_ctls[i].parent_nid,
+ ca0132_tuning_ctls[i].nid,
+ ca0132_tuning_ctls[i].name,
+ ca0132_tuning_ctls[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void ca0132_init_tuning_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+
+ /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */
+ spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10;
+ /* SVM level defaults to 0.74. */
+ spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74;
+
+ /* EQ defaults to 0dB. */
+ for (i = 2; i < TUNING_CTLS_COUNT; i++)
+ spec->cur_ctl_vals[i] = 24;
+}
+#endif
+
+static int ca0132_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int pin_ctl;
+ int jack_present;
+ int auto_jack;
+ unsigned int tmp;
+ int err;
+
+ CA0132_LOG("ca0132_select_out\n");
+
+ snd_hda_power_up(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]);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ CA0132_LOG("ca0132_select_out speaker\n");
+ /*speaker out config*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_param(codec, 0x80, 0x04,
+ &tmp, sizeof(unsigned int));
+ if (err < 0)
+ goto exit;
+ /*enable speaker EQ*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_param(codec, 0x8f, 0x00,
+ &tmp, sizeof(unsigned int));
+ if (err < 0)
+ goto exit;
+ /*speaker EQ bypass attenuation is 0*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_param(codec, 0x8f, 0x01,
+ &tmp, sizeof(unsigned int));
+ if (err < 0)
+ goto exit;
+
+ /* disable headphone node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_ctl & 0xBF);
+ /* disable headphone EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ /* enable speaker node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_ctl | 0x40);
+ /* enable speaker EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ } else {
+ CA0132_LOG("ca0132_select_out hp\n");
+ /*headphone out config*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_param(codec, 0x80, 0x04,
+ &tmp, sizeof(unsigned int));
+ if (err < 0)
+ goto exit;
+ /*disable speaker EQ*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_param(codec, 0x8f, 0x00,
+ &tmp, sizeof(unsigned int));
+ if (err < 0)
+ goto exit;
+ /*speaker EQ bypass attenuation is -5.0*/
+ tmp = FLOAT_MINUS_5;
+ err = dspio_set_param(codec, 0x8f, 0x01,
+ &tmp, sizeof(unsigned int));
+ if (err < 0)
+ goto exit;
+
+ /* disable speaker*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_ctl & 0xBF);
+ /* disable speaker EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ /* enable headphone*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_ctl | 0x40);
+ /* enable headphone EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ }
+
+exit:
+ snd_hda_power_down(codec);
+
+ return err < 0 ? err : 0;
+}
+
+static void ca0132_set_dmic(struct hda_codec *codec, int enable);
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+
+static int ca0132_select_mic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int jack_present;
+ int auto_jack;
+
+ CA0132_LOG("ca0132_select_mic\n");
+
+ snd_hda_power_up(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]);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_mic_type = LINE_MIC_IN;
+ else
+ spec->cur_mic_type = DIGITAL_MIC;
+
+ if (spec->cur_mic_type == DIGITAL_MIC) {
+ /* enable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000);
+ ca0132_set_dmic(codec, 1);
+ ca0132_mic_boost_set(codec, 0);
+ /* set voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS,
+ spec->effects_switch
+ [VOICE_FOCUS - EFFECT_START_NID]);
+ } else {
+ /* disable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000);
+ ca0132_set_dmic(codec, 0);
+ ca0132_mic_boost_set(codec, spec->cur_mic_boost);
+ /* disable voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS, 0);
+ }
+
+ snd_hda_power_down(codec);
return 0;
}
/*
- * Digital out
+ * Check if VNODE settings take effect immediately.
+ */
+static bool ca0132_is_vnode_effective(struct hda_codec *codec,
+ hda_nid_t vnid,
+ hda_nid_t *shared_nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ bool effective = false;
+
+ switch (vnid) {
+ case VNID_SPK:
+ nid = spec->shared_out_nid;
+ effective = true;
+ break;
+ case VNID_MIC:
+ nid = spec->shared_mic_nid;
+ effective = true;
+ break;
+ default:
+ break;
+ }
+
+ if (effective && shared_nid)
+ *shared_nid = nid;
+
+ return effective;
+}
+
+/*
+ * The following functions are control change helpers.
+ * They return 0 if no changed. Return 1 if changed.
*/
-static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ /* based on CrystalVoice state to enable VoiceFX. */
+ if (enable) {
+ tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ?
+ FLOAT_ONE : FLOAT_ZERO;
+ } else {
+ tmp = FLOAT_ZERO;
+ }
+
+ dspio_set_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[0], &tmp, sizeof(unsigned int));
+
+ return 1;
+}
+
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int on;
+ int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ int err = 0;
+ int idx = nid - EFFECT_START_NID;
+
+ if ((idx < 0) || (idx >= num_fx))
+ return 0; /* no changed */
+
+ /* for out effect, qualify with PE */
+ if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) {
+ /* if PE if off, turn off out effects. */
+ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ val = 0;
+ }
+
+ /* for in effect, qualify with CrystalVoice */
+ if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) {
+ /* if CrystalVoice if off, turn off in effects. */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ val = 0;
+
+ /* Voice Focus applies to 2-ch Mic, Digital Mic */
+ if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
+ val = 0;
+ }
+
+ CA0132_LOG("ca0132_effect_set: nid=0x%x, val=%ld\n", nid, val);
+
+ on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[0],
+ &on, sizeof(unsigned int));
+ if (err < 0)
+ return 0; /* no changed */
+
+ return 1;
+}
+
+static int ca0132_pe_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+
+ CA0132_LOG("ca0132_pe_switch_set: val=%ld\n",
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
+
+ i = OUT_EFFECT_START_NID - EFFECT_START_NID;
+ nid = OUT_EFFECT_START_NID;
+ /* PE affects all out effects */
+ for (; nid < OUT_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ return ret;
+}
+
+static int ca0132_cvoice_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+
+ CA0132_LOG("ca0132_cvoice_switch_set: val=%ld\n",
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]);
+
+ i = IN_EFFECT_START_NID - EFFECT_START_NID;
+ nid = IN_EFFECT_START_NID;
+ /* CrystalVoice affects all in effects */
+ for (; nid < IN_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ /* including VoiceFX */
+ ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0));
+ return ret;
+}
+
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ if (val) /* on */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 3);
+ else /* off */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 0);
+
+ return ret;
+}
+
+static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int ret = 0;
struct ca0132_spec *spec = codec->spec;
+ int auto_jack;
+
+ if (nid == VNID_HP_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+ if (!auto_jack)
+ ca0132_select_out(codec);
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+ if (!auto_jack)
+ ca0132_select_mic(codec);
+ return 1;
+ }
- ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
+ if (nid == VNID_HP_ASEL) {
+ ca0132_select_out(codec);
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_ASEL) {
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ unsigned long pval;
+
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ }
+
+ return ret;
+}
+/* End of control change helpers. */
+
+static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = sizeof(ca0132_voicefx_presets)
+ / sizeof(struct ct_voicefx_preset);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ca0132_voicefx_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->voicefx_val;
return 0;
}
-static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = sizeof(ca0132_voicefx_presets)
+ / sizeof(struct ct_voicefx_preset);
+
+ if (sel >= items)
+ return 0;
+
+ CA0132_LOG("ca0132_voicefx_put: sel=%d, preset=%s\n",
+ sel, ca0132_voicefx_presets[sel].name);
+
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[i],
+ &(ca0132_voicefx_presets[sel].vals[i]),
+ sizeof(unsigned int));
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0) {
+ spec->voicefx_val = sel;
+ /* enable voice fx */
+ ca0132_voicefx_set(codec, (sel ? 1 : 0));
+ }
+
+ return 1;
+}
+
+static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ *valp = spec->vnode_lswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+ }
+
+ /* effects, include PE and CrystalVoice */
+ if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) {
+ *valp = spec->effects_switch[nid - EFFECT_START_NID];
+ return 0;
+ }
- ca0132_cleanup_stream(codec, spec->dig_out);
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ *valp = spec->cur_mic_boost;
+ return 0;
+ }
return 0;
}
+static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int changed = 1;
+
+ CA0132_LOG("ca0132_switch_put: nid=0x%x, val=%ld\n", nid, *valp);
+
+ snd_hda_power_up(codec);
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ spec->vnode_lswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ changed = ca0132_vnode_switch_set(kcontrol, ucontrol);
+ goto exit;
+ }
+
+ /* PE */
+ if (nid == PLAY_ENHANCEMENT) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_pe_switch_set(codec);
+ goto exit;
+ }
+
+ /* CrystalVoice */
+ if (nid == CRYSTAL_VOICE) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_cvoice_switch_set(codec);
+ goto exit;
+ }
+
+ /* out and in effects */
+ if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) ||
+ ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_effects_set(codec, nid, *valp);
+ goto exit;
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ spec->cur_mic_boost = *valp;
+
+ /* Mic boost does not apply to Digital Mic */
+ if (spec->cur_mic_type != DIGITAL_MIC)
+ changed = ca0132_mic_boost_set(codec, *valp);
+ goto exit;
+ }
+
+exit:
+ snd_hda_power_down(codec);
+ return changed;
+}
+
/*
- * Analog capture
+ * Volume related
*/
-static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out info */
+ nid = spec->shared_out_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ case VNID_MIC:
+ /* follow shared_mic info */
+ nid = spec->shared_mic_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ default:
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ }
+ return err;
+}
- ca0132_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
+static int ca0132_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ /* store the left and right volume */
+ if (ch & 1) {
+ *valp = spec->vnode_lvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rvol[nid - VNODE_START_NID];
+ valp++;
+ }
return 0;
}
-static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int changed = 1;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+
+ snd_hda_power_up(codec);
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_power_down(codec);
+ }
+
+ return changed;
+}
+
+static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out tlv */
+ nid = spec->shared_out_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ case VNID_MIC:
+ /* follow shared_mic tlv */
+ nid = spec->shared_mic_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ default:
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ }
+ return err;
+}
+
+static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_voicefx(struct hda_codec *codec)
{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
-
- return 0;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
+ VOICEFX, 1, 0, HDA_INPUT);
+ knew.info = ca0132_voicefx_info;
+ knew.get = ca0132_voicefx_get;
+ knew.put = ca0132_voicefx_put;
+ return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
}
/*
- * Digital capture
+ * When changing Node IDs for Mixer Controls below, make sure to update
+ * Node IDs in ca0132_config() as well.
*/
-static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static struct snd_kcontrol_new ca0132_mixer[] = {
+ CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch",
+ 0x12, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch",
+ VNID_HP_SEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch",
+ VNID_AMIC1_SEL, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch",
+ VNID_AMIC1_ASEL, 1, HDA_INPUT),
+ { } /* end */
+};
+
+static int ca0132_build_controls(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
+ int i, num_fx;
+ int err = 0;
- ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
+ /* Add Mixer controls */
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
- return 0;
-}
+ /* Add in and out effects controls.
+ * VoiceFX, PE and CrystalVoice are added separately.
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ err = add_fx_switch(codec, ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
-static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1);
+ if (err < 0)
+ return err;
+
+ add_voicefx(codec);
- ca0132_cleanup_stream(codec, spec->dig_in);
+ #ifdef ENABLE_TUNING_CONTROLS
+ add_tuning_ctls(codec);
+ #endif
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
return 0;
}
/*
+ * PCM
*/
static struct hda_pcm_stream ca0132_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 6,
.ops = {
.prepare = ca0132_playback_pcm_prepare,
.cleanup = ca0132_playback_pcm_cleanup
@@ -636,26 +3902,6 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = {
},
};
-static struct hda_pcm_stream ca0132_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0132_dig_playback_pcm_prepare,
- .cleanup = ca0132_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ca0132_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0132_dig_capture_pcm_prepare,
- .cleanup = ca0132_dig_capture_pcm_cleanup
- },
-};
-
static int ca0132_build_pcms(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
@@ -668,391 +3914,548 @@ static int ca0132_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
+ spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
codec->num_pcms++;
- if (!spec->dig_out && !spec->dig_in)
- return 0;
+ info++;
+ info->name = "CA0132 Analog Mic-In2";
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+ codec->num_pcms++;
info++;
- info->name = "CA0132 Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->dig_out) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- ca0132_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- ca0132_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
+ info->name = "CA0132 What U Hear";
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
codec->num_pcms++;
return 0;
}
-#define REG_CODEC_MUTE 0x18b014
-#define REG_CODEC_HP_VOL_L 0x18b070
-#define REG_CODEC_HP_VOL_R 0x18b074
-
-static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
-
- *valp = spec->curr_hp_switch;
- return 0;
+ if (pin) {
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac)
+ snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
}
-static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
- unsigned int data;
- int err;
-
- /* any change? */
- if (spec->curr_hp_switch == *valp)
- return 0;
-
- snd_hda_power_up(codec);
+ if (pin) {
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_VREF80);
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc) {
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
- err = chipio_read(codec, REG_CODEC_MUTE, &data);
- if (err < 0)
- goto exit;
+ /* init to 0 dB and unmute. */
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_VOLMASK, 0x5a);
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_MUTE, 0);
+ }
+}
- /* *valp 0 is mute, 1 is unmute */
- data = (data & 0x7f) | (*valp ? 0 : 0x80);
- err = chipio_write(codec, REG_CODEC_MUTE, data);
- if (err < 0)
- goto exit;
+static void ca0132_init_unsol(struct hda_codec *codec)
+{
+ snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP);
+ snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1);
+}
- spec->curr_hp_switch = *valp;
+static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int caps;
- exit:
- snd_hda_power_down(codec);
- return err < 0 ? err : 1;
+ caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ snd_hda_override_amp_caps(codec, nid, dir, caps);
}
-static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ca0132_set_dmic(struct hda_codec *codec, int enable)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
-
- *valp = spec->curr_speaker_switch;
- return 0;
+ unsigned int tmp;
+ u8 val;
+
+ CA0132_LOG("ca0132_set_dmic: enable=%d\n", enable);
+
+ if (enable) {
+ /* set DMic input as 2-ch */
+ tmp = FLOAT_TWO;
+ dspio_set_param(codec, 0x80, 0x00, &tmp, sizeof(unsigned int));
+
+ val = spec->dmic_ctl;
+ val |= 0x80;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1);
+ } else {
+ /* set AMic input as mono */
+ tmp = FLOAT_ONE;
+ dspio_set_param(codec, 0x80, 0x00, &tmp, sizeof(unsigned int));
+
+ val = spec->dmic_ctl;
+ /* clear bit7 and bit5 to disable dmic */
+ val &= 0x5f;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0);
+ }
}
-static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ca0132_init_dmic(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
- unsigned int data;
- int err;
-
- /* any change? */
- if (spec->curr_speaker_switch == *valp)
- return 0;
-
- snd_hda_power_up(codec);
-
- err = chipio_read(codec, REG_CODEC_MUTE, &data);
- if (err < 0)
- goto exit;
-
- /* *valp 0 is mute, 1 is unmute */
- data = (data & 0xef) | (*valp ? 0 : 0x10);
- err = chipio_write(codec, REG_CODEC_MUTE, data);
- if (err < 0)
- goto exit;
-
- spec->curr_speaker_switch = *valp;
-
- exit:
- snd_hda_power_down(codec);
- return err < 0 ? err : 1;
+ u8 val;
+
+ /* Setup Digital Mic here, but don't enable.
+ * Enable based on jack detect.
+ */
+
+ /* MCLK uses MPIO1, set to enable.
+ * Bit 2-0: MPIO select
+ * Bit 3: set to disable
+ * Bit 7-4: reserved
+ */
+ val = 0x01;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_MCLK_SET, val);
+
+ /* Data1 uses MPIO3. Data2 not use
+ * Bit 2-0: Data1 MPIO select
+ * Bit 3: set disable Data1
+ * Bit 6-4: Data2 MPIO select
+ * Bit 7: set disable Data2
+ */
+ val = 0x83;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_PIN_SET, val);
+
+ /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first.
+ * Bit 3-0: Channel mask
+ * Bit 4: set for 48KHz, clear for 32KHz
+ * Bit 5: mode
+ * Bit 6: set to select Data2, clear for Data1
+ * Bit 7: set to enable DMic, clear for AMic
+ */
+ val = 0x23;
+ /* keep a copy of dmic ctl val for enable/disable dmic purpuse */
+ spec->dmic_ctl = val;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
}
-static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ca0132_init_analog_mic2(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
- *valp++ = spec->curr_hp_volume[0];
- *valp = spec->curr_hp_volume[1];
- return 0;
+ mutex_lock(&spec->chipio_mutex);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+ mutex_unlock(&spec->chipio_mutex);
}
-static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ca0132_refresh_widget_caps(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- long *valp = ucontrol->value.integer.value;
- long left_vol, right_vol;
- unsigned int data;
- int val;
- int err;
+ int i;
+ hda_nid_t nid;
- left_vol = *valp++;
- right_vol = *valp;
+ CA0132_LOG("ca0132_refresh_widget_caps.\n");
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++)
+ codec->wcaps[i] = snd_hda_param_read(codec, nid,
+ AC_PAR_AUDIO_WIDGET_CAP);
- /* any change? */
- if ((spec->curr_hp_volume[0] == left_vol) &&
- (spec->curr_hp_volume[1] == right_vol))
- return 0;
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT);
- snd_hda_power_up(codec);
+ for (i = 0; i < spec->num_outputs; i++)
+ refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT);
- err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data);
- if (err < 0)
- goto exit;
+ for (i = 0; i < spec->num_inputs; i++) {
+ refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT);
+ refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT);
+ }
+}
- val = 31 - left_vol;
- data = (data & 0xe0) | val;
- err = chipio_write(codec, REG_CODEC_HP_VOL_L, data);
- if (err < 0)
- goto exit;
+static void ca0132_setup_defaults(struct hda_codec *codec)
+{
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
- val = 31 - right_vol;
- data = (data & 0xe0) | val;
- err = chipio_write(codec, REG_CODEC_HP_VOL_R, data);
- if (err < 0)
- goto exit;
+ if (!dspload_is_loaded(codec))
+ return;
- spec->curr_hp_volume[0] = left_vol;
- spec->curr_hp_volume[1] = right_vol;
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ &(ca0132_effects[idx].def_vals[i]),
+ sizeof(unsigned int));
+ }
+ }
- exit:
- snd_hda_power_down(codec);
- return err < 0 ? err : 1;
-}
+ /* set AMic1 and AMic2 as mono mic */
+ tmp = FLOAT_ONE;
+ dspio_set_param(codec, 0x80, 0x00, &tmp, sizeof(unsigned int));
+ dspio_set_param(codec, 0x80, 0x01, &tmp, sizeof(unsigned int));
-static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
-{
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
- nid, 1, 0, HDA_OUTPUT);
- knew.get = ca0132_hp_switch_get;
- knew.put = ca0132_hp_switch_put;
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ /* set AMic1 as CrystalVoice input */
+ tmp = FLOAT_ONE;
+ dspio_set_param(codec, 0x80, 0x05, &tmp, sizeof(unsigned int));
}
-static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
+static void ca0132_init_flags(struct hda_codec *codec)
{
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
- nid, 3, 0, HDA_OUTPUT);
- knew.get = ca0132_hp_volume_get;
- knew.put = ca0132_hp_volume_put;
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
}
-static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
+static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
{
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
- nid, 1, 0, HDA_OUTPUT);
- knew.get = ca0132_speaker_switch_get;
- knew.put = ca0132_speaker_switch_put;
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
}
-static void ca0132_fix_hp_caps(struct hda_codec *codec)
+static bool ca0132_download_dsp_images(struct hda_codec *codec)
{
- struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps;
+ bool dsp_loaded = false;
+ const struct dsp_image_seg *dsp_os_image;
+ const struct firmware *fw_entry;
+
+ if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
+ return false;
+
+ dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+ dspload_image(codec, dsp_os_image, 0, 0, true, 0);
+ dsp_loaded = dspload_wait_loaded(codec);
+
+ release_firmware(fw_entry);
- /* set mute-capable, 1db step, 32 steps, ofs 6 */
- caps = 0x80031f06;
- snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps);
+ if (dsp_loaded)
+ dspload_speakereq(codec);
+
+ return dsp_loaded;
}
-static int ca0132_build_controls(struct hda_codec *codec)
+static void ca0132_download_dsp(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
- if (spec->multiout.num_dacs) {
- err = add_speaker_switch(codec, spec->out_pins[0]);
- if (err < 0)
- return err;
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ /* check if there is power cut-off to DSP */
+ if (dspload_is_loaded(codec)) {
+ /* dsp is already loaded. */
+ /*spec->dsp_state = DSP_DOWNLOADED;*/
}
- if (cfg->hp_outs) {
- ca0132_fix_hp_caps(codec);
- err = add_hp_switch(codec, cfg->hp_pins[0]);
- if (err < 0)
- return err;
- err = add_hp_volume(codec, cfg->hp_pins[0]);
- if (err < 0)
- return err;
+ if (spec->dsp_state == DSP_DOWNLOAD_INIT) {
+ chipio_enable_clocks(codec);
+ spec->dsp_state = DSP_DOWNLOADING;
+ if (!ca0132_download_dsp_images(codec))
+ spec->dsp_state = DSP_DOWNLOAD_FAILED;
+ else
+ spec->dsp_state = DSP_DOWNLOADED;
}
- for (i = 0; i < spec->num_inputs; i++) {
- const char *label = spec->input_labels[i];
+ if (spec->dsp_state == DSP_DOWNLOADED)
+ ca0132_set_dsp_msr(codec, true);
+}
- err = add_in_switch(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- /* add Mic-Boost */
- err = add_in_mono_volume(codec, spec->input_pins[i],
- "Mic Boost", 1);
- if (err < 0)
- return err;
- }
- }
+static void ca0132_process_dsp_response(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
- if (spec->dig_out) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
- spec->dig_out);
- if (err < 0)
- return err;
- err = add_out_volume(codec, spec->dig_out, "IEC958");
- if (err < 0)
- return err;
+ CA0132_LOG("ca0132_process_dsp_response\n");
+ if (spec->wait_scp) {
+ if (dspio_get_response_data(codec) >= 0)
+ spec->wait_scp = 0;
}
- if (spec->dig_in) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->dig_in, "IEC958");
- if (err < 0)
- return err;
- }
- return 0;
+ dspio_clear_response_queue(codec);
}
-
-static void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
+static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
{
- /* Set Creative extension */
- snd_printdd("SET CREATIVE EXTENSION\n");
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE,
- enable);
- msleep(20);
+ CA0132_LOG("ca0132_unsol_event: 0x%x\n", res);
+
+
+ if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) {
+ ca0132_process_dsp_response(codec);
+ } else {
+ res = snd_hda_jack_get_action(codec,
+ (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f);
+
+ CA0132_LOG("snd_hda_jack_get_action: 0x%x\n", res);
+
+ switch (res) {
+ case UNSOL_TAG_HP:
+ ca0132_select_out(codec);
+ break;
+ case UNSOL_TAG_AMIC1:
+ ca0132_select_mic(codec);
+ break;
+ default:
+ break;
+ }
+ snd_hda_jack_report_sync(codec);
+ }
}
+/*
+ * Verbs tables.
+ */
-static void ca0132_config(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- /* line-outs */
- cfg->line_outs = 1;
- cfg->line_out_pins[0] = 0x0b; /* front */
- cfg->line_out_type = AUTO_PIN_LINE_OUT;
-
- spec->dacs[0] = 0x02;
- spec->out_pins[0] = 0x0b;
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = 1;
- spec->multiout.max_channels = 2;
-
- /* headphone */
- cfg->hp_outs = 1;
- cfg->hp_pins[0] = 0x0f;
-
- spec->hp_dac = 0;
- spec->multiout.hp_nid = 0;
+/* Sends before DSP download. */
+static struct hda_verb ca0132_base_init_verbs[] = {
+ /*enable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
+ /*enable DSP node unsol, needed for DSP download*/
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP},
+ {}
+};
- /* inputs */
- cfg->num_inputs = 2; /* Mic-in and line-in */
- cfg->inputs[0].pin = 0x12;
- cfg->inputs[0].type = AUTO_PIN_MIC;
- cfg->inputs[1].pin = 0x11;
- cfg->inputs[1].type = AUTO_PIN_LINE_IN;
+/* Send at exit. */
+static struct hda_verb ca0132_base_exit_verbs[] = {
+ /*set afg to D3*/
+ {0x01, AC_VERB_SET_POWER_STATE, 0x03},
+ /*disable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0},
+ {}
+};
- /* Mic-in */
- spec->input_pins[0] = 0x12;
- spec->input_labels[0] = "Mic-In";
- spec->adcs[0] = 0x07;
+/* Other verbs tables. Sends after DSP download. */
+static struct hda_verb ca0132_init_verbs0[] = {
+ /* chip init verbs */
+ {0x15, 0x70D, 0xF0},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x53},
+ {0x15, 0x707, 0xD4},
+ {0x15, 0x707, 0xEF},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x37},
+ {0x15, 0x707, 0x78},
+ {0x15, 0x53C, 0xCE},
+ {0x15, 0x575, 0xC9},
+ {0x15, 0x53D, 0xCE},
+ {0x15, 0x5B7, 0xC9},
+ {0x15, 0x70D, 0xE8},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x68},
+ {0x15, 0x707, 0x62},
+ {0x15, 0x53A, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x53B, 0xCE},
+ {0x15, 0x5E8, 0xC9},
+ {0x15, 0x717, 0x0D},
+ {0x15, 0x718, 0x20},
+ {}
+};
- /* Line-In */
- spec->input_pins[1] = 0x11;
- spec->input_labels[1] = "Line-In";
- spec->adcs[1] = 0x08;
- spec->num_inputs = 2;
-}
+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;
+ int num_fx;
+ int i;
+ unsigned int on;
mutex_init(&spec->chipio_mutex);
+
+ spec->cur_out_type = SPEAKER_OUT;
+ spec->cur_mic_type = DIGITAL_MIC;
+ spec->cur_mic_boost = 0;
+
+ for (i = 0; i < VNODES_COUNT; i++) {
+ spec->vnode_lvol[i] = 0x5a;
+ spec->vnode_rvol[i] = 0x5a;
+ spec->vnode_lswitch[i] = 0;
+ spec->vnode_rswitch[i] = 0;
+ }
+
+ /*
+ * Default states for effects are in ca0132_effects[].
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ on = (unsigned int)ca0132_effects[i].reqs[0];
+ spec->effects_switch[i] = on ? 1 : 0;
+ }
+
+ spec->voicefx_val = 0;
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
+
+ #ifdef ENABLE_TUNING_CONTROLS
+ ca0132_init_tuning_defaults(codec);
+ #endif
}
static void ca0132_exit_chip(struct hda_codec *codec)
{
/* put any chip cleanup stuffs here. */
+
+ if (dspload_is_loaded(codec))
+ dsp_reset(codec);
}
static int ca0132_init(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- init_output(codec, spec->out_pins[i],
- spec->multiout.dac_nids[i]);
- }
- init_output(codec, cfg->hp_pins[0], spec->hp_dac);
- init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ spec->curr_chip_addx = (unsigned int)INVALID_CHIP_ADDRESS;
+
+ snd_hda_power_up(codec);
+
+ ca0132_init_flags(codec);
+ snd_hda_sequence_write(codec, spec->base_init_verbs);
+ ca0132_download_dsp(codec);
+ ca0132_refresh_widget_caps(codec);
+ ca0132_setup_defaults(codec);
+ ca0132_init_analog_mic2(codec);
+ ca0132_init_dmic(codec);
+
+ for (i = 0; i < spec->num_outputs; i++)
+ init_output(codec, spec->out_pins[i], spec->dacs[0]);
for (i = 0; i < spec->num_inputs; i++)
init_input(codec, spec->input_pins[i], spec->adcs[i]);
- 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]);
- ca0132_set_ct_ext(codec, 1);
+ ca0132_init_unsol(codec);
+
+ ca0132_select_out(codec);
+ ca0132_select_mic(codec);
+
+ snd_hda_jack_report_sync(codec);
+
+ snd_hda_power_down(codec);
return 0;
}
-
static void ca0132_free(struct hda_codec *codec)
{
- ca0132_set_ct_ext(codec, 0);
+ struct ca0132_spec *spec = codec->spec;
+
+ snd_hda_power_up(codec);
+ snd_hda_sequence_write(codec, spec->base_exit_verbs);
ca0132_exit_chip(codec);
+ snd_hda_power_down(codec);
kfree(codec->spec);
}
+
static struct hda_codec_ops ca0132_patch_ops = {
.build_controls = ca0132_build_controls,
.build_pcms = ca0132_build_pcms,
.init = ca0132_init,
.free = ca0132_free,
+ .unsol_event = ca0132_unsol_event,
};
+static void ca0132_config(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ spec->dacs[0] = 0x2;
+ spec->dacs[1] = 0x3;
+ spec->dacs[2] = 0x4;
+
+ spec->multiout.dac_nids = spec->dacs;
+ 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;
+ spec->num_inputs = 3;
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+ spec->shared_mic_nid = 0x7;
+
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+}
static int patch_ca0132(struct hda_codec *codec)
{
struct ca0132_spec *spec;
+ int err;
snd_printdd("patch_ca0132\n");
@@ -1061,27 +4464,41 @@ static int patch_ca0132(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ 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;
+
ca0132_init_chip(codec);
ca0132_config(codec);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
codec->patch_ops = ca0132_patch_ops;
return 0;
}
+
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_ca0132[] = {
- { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
+ { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:11020011");
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec");
+MODULE_DESCRIPTION("Creative Sound Core3D codec");
static struct hda_codec_preset_list ca0132_list = {
.preset = snd_hda_preset_ca0132,
--
1.7.4.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCHv2 - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore()
2012-07-25 18:01 ` [PATCHv2 " Ian Minett
@ 2012-07-25 18:01 ` Ian Minett
2012-07-26 8:06 ` [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features Takashi Iwai
1 sibling, 0 replies; 21+ messages in thread
From: Ian Minett @ 2012-07-25 18:01 UTC (permalink / raw)
To: patch; +Cc: alsa-devel, Ian Minett
From: Ian Minett <ian_minett@creativelabs.com>
in azx_pcm_trigger().
This change is to prevent deadlock when an interrupt occurs,
caused by chip->reg_lock contention.
Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 796472d..0ccffde 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -551,6 +551,7 @@ enum {
(AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
AZX_DCAPS_ALIGN_BUFSIZE)
+/* quirks for Creative CTHDA */
#define AZX_DCAPS_PRESET_CTHDA \
(AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
@@ -1930,6 +1931,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_substream *s;
int rstart = 0, start, nsync = 0, sbits = 0;
int nwait, timeout;
+ unsigned long flags;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -1956,7 +1958,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_trigger_done(s, substream);
}
- spin_lock(&chip->reg_lock);
+ spin_lock_irqsave(&chip->reg_lock, flags);
if (nsync > 1) {
/* first, set SYNC bits of corresponding streams */
if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
@@ -1980,7 +1982,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
azx_dev->running = start;
}
- spin_unlock(&chip->reg_lock);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
if (start) {
if (nsync == 1)
return 0;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-07-25 18:01 ` [PATCHv2 " Ian Minett
2012-07-25 18:01 ` [PATCHv2 - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore() Ian Minett
@ 2012-07-26 8:06 ` Takashi Iwai
[not found] ` <OF0CFC3DA9.A9100453-ON88257A48.006BF087-88257A48.006FCB42@cli.creative.com>
1 sibling, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-07-26 8:06 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Wed, 25 Jul 2012 11:01:19 -0700,
Ian Minett wrote:
>
> From: Ian Minett <ian_minett@creativelabs.com>
>
> Hi,
> Thanks again for the feedback - we've reworked the patch accordingly, to remove
> floats, tidy the use of macros and add the MODULE_FIRMWARE() lines.
>
> You are right - since it is a pretty big change introducing all the DSP
> functionality it isn't trivial to break it into multiple small patches.
> I've added more info on what has been changed below in the git commit message.
>
> The reason we need to modify the trigger callback in hda_intel.c is that the DSP
> code is downloaded from the host (driver) to the CA0132 chip via the HD-link.
> This means that the trigger callback is called from the codec driver.
Well, this is the biggest problem. What the patch does is a layer
violation. Such an operation must not be performed in the codec
driver. I guess you know it very well when you look again how ugly
hacks you need for setting up a fake PCM instance and so on.
One possible way would be to move these DSP loading code into
hda_intel.c. But then it bloats up the generic controller driver.
Another possibility is to fork hda_intel.c and move the Creative's
controller chip driver there (not meant the code in patch_ca0132.c).
You can strip down many workarounds found in hda_intel.c
(e.g. bdl_pos_adj, position_fix, probing errors, vga_switcheroo, etc)
as a bonus. And the new controller driver can still use the same
hda_codec.c and the rest as long as it uses the same structure.
> I will submit the DSP firmware blob (ctefx.bin) as a separate patch against the
> alsa-firmware repository, for intended upstreaming to linux-firmware package.
> The CA0132 codec uses request_firmware() loader to find the DSP binary in the
> /lib/firmware directory (the firmware isn't required for building the CA0132
> module). If this isn't the correct way to submit firmware binaries, we'd
> appreciate any info on what the correct method for this is.
Yes, this is the correct way.
But, still one concern is that people may very likely build the kernel
without taking the firmware. Then suddenly it breaks just by the
kernel update -- this shouldn't happen.
So, we'd need a kernel config, or at least a module parameter, to
enable/disable the new DSP stuff. When it's off, the whole DSP isn't
requested and it behaves just likes a standard HD-audio.
thanks,
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
[not found] ` <OF0CFC3DA9.A9100453-ON88257A48.006BF087-88257A48.006FCB42@cli.creative.com>
@ 2012-07-28 5:57 ` Takashi Iwai
[not found] ` <OF301523C9.1A0D8E05-ON88257A4B.0074BC06-88257A4B.007D5630@cli.creative.com>
0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-07-28 5:57 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Fri, 27 Jul 2012 13:21:05 -0700,
Ian Minett wrote:
>
> >Another possibility is to fork hda_intel.c and move the Creative's
> >controller chip driver there (not meant the code in patch_ca0132.c).
> >You can strip down many workarounds found in hda_intel.c
> >(e.g. bdl_pos_adj, position_fix, probing errors, vga_switcheroo, etc)
> >as a bonus. And the new controller driver can still use the same
> >hda_codec.c and the rest as long as it uses the same structure.
>
> The CA0132 chip can be connected to and work with any other, non-
> Creative HDA controllers. So, forking hda_intel.c for a Creative-
> specific controller won't work in the case where our chip is using
> a different HDA controller.
So, the DSP part is specific to the codec and not the controller?
OK, then forking the controller doesn't work.
> Do you have any suggestions or thoughts on how we can add the
> spin_lock_irqxxx(), or an alternative approach, into non-specific
> controller code in order to facilitate the codec DSP download? Since
> the download isn't controller-specific, it doesn't sound right to move
> it into controller code such as hda_intel.c either.
No, no. The biggest problem is that the codec driver is calling what
the controller driver should do -- prepare, trigger, whatever --
manually. This is the layer violation.
> One thought we have is to handle the spin_lock_irqxxx() as a special
> case or quirk for the CA0132 codec. Using an approach similar to
> 'needs_damn_long_delay' in the CA0110 patch, we could define another
> flag e.g. 'needs_trigger_irqsave' in hda_codec.h struct hda_bus{}. In
> the CA0132 patch, we would set this flag, which would then be read in
> hda_intel.c,azx_pcm_trigger(). Either spin_lock_irqxxx() or the current
> spin_lock() would then be called depending on the flag condition.
> What is your opinion on an approach like this? Would this be acceptable?
No. The fact that you are calling the PCM functions is already
wrong. The irqsave() is just a side-effect by that, and we can forget
about it completely now.
The only question is where to implement the DSP loader stuff and how.
> >But, still one concern is that people may very likely build the kernel
> >without taking the firmware. Then suddenly it breaks just by the
> >kernel update -- this shouldn't happen.
> >So, we'd need a kernel config, or at least a module parameter, to
> >enable/disable the new DSP stuff. When it's off, the whole DSP isn't
> >requested and it behaves just likes a standard HD-audio.
>
> The driver allows for audio playback to still function as normal in
> the absence of the DSP firmware image, the only effect will be that
> there will be no DSP processing applied to the stream. The DSP effect
> and volume controls and WUH recording capability won't be available.
Then we'd need some flag to enable/disable firmware loading, too.
> Finally, can I send the binary firmware patch to the list? I sent it
> shortly after the previous patch submission but it didn't appear to get
> through. Is it ok to send directly to you?
It's up to you. If the file is too big, you can send it directly to
me and I'll push it to git tree.
thanks,
Takashi
>
> Thanks very much,
> Ian
>
>
>
>
>
> Takashi Iwai
> <tiwai@suse.de>
> To
> 07/26/2012 01:06 Ian Minett <ian_minett@creativelabs.com>
> AM cc
> alsa-devel@alsa-project.org
> Subject
> Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative
> CA0132 codec to add DSP features.
>
>
>
>
>
>
>
>
>
>
> At Wed, 25 Jul 2012 11:01:19 -0700,
> Ian Minett wrote:
> >
> > From: Ian Minett <ian_minett@creativelabs.com>
> >
> > Hi,
> > Thanks again for the feedback - we've reworked the patch accordingly, to remove
> > floats, tidy the use of macros and add the MODULE_FIRMWARE() lines.
> >
> > You are right - since it is a pretty big change introducing all the DSP
> > functionality it isn't trivial to break it into multiple small patches.
> > I've added more info on what has been changed below in the git commit message.
> >
> > The reason we need to modify the trigger callback in hda_intel.c is that the DSP
> > code is downloaded from the host (driver) to the CA0132 chip via the HD-link.
> > This means that the trigger callback is called from the codec driver.
>
> Well, this is the biggest problem. What the patch does is a layer
> violation. Such an operation must not be performed in the codec
> driver. I guess you know it very well when you look again how ugly
> hacks you need for setting up a fake PCM instance and so on.
>
> One possible way would be to move these DSP loading code into
> hda_intel.c. But then it bloats up the generic controller driver.
>
> Another possibility is to fork hda_intel.c and move the Creative's
> controller chip driver there (not meant the code in patch_ca0132.c).
> You can strip down many workarounds found in hda_intel.c
> (e.g. bdl_pos_adj, position_fix, probing errors, vga_switcheroo, etc)
> as a bonus. And the new controller driver can still use the same
> hda_codec.c and the rest as long as it uses the same structure.
>
> > I will submit the DSP firmware blob (ctefx.bin) as a separate patch against the
> > alsa-firmware repository, for intended upstreaming to linux-firmware package.
> > The CA0132 codec uses request_firmware() loader to find the DSP binary in the
> > /lib/firmware directory (the firmware isn't required for building the CA0132
> > module). If this isn't the correct way to submit firmware binaries, we'd
> > appreciate any info on what the correct method for this is.
>
> Yes, this is the correct way.
>
> But, still one concern is that people may very likely build the kernel
> without taking the firmware. Then suddenly it breaks just by the
> kernel update -- this shouldn't happen.
>
> So, we'd need a kernel config, or at least a module parameter, to
> enable/disable the new DSP stuff. When it's off, the whole DSP isn't
> requested and it behaves just likes a standard HD-audio.
>
>
> thanks,
>
> Takashi
>
> ForwardSourceID:NT0001C0EA
> [1.2 <text/html; US-ASCII (quoted-printable)>]
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
[not found] ` <OF301523C9.1A0D8E05-ON88257A4B.0074BC06-88257A4B.007D5630@cli.creative.com>
@ 2012-07-31 15:15 ` Takashi Iwai
2012-08-01 2:38 ` Ian Minett
0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-07-31 15:15 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Mon, 30 Jul 2012 15:49:00 -0700,
Ian Minett wrote:
>
> >> One thought we have is to handle the spin_lock_irqxxx() as a special
> >> case or quirk for the CA0132 codec. Using an approach similar to
> >> 'needs_damn_long_delay' in the CA0110 patch, we could define another
> >> flag e.g. 'needs_trigger_irqsave' in hda_codec.h struct hda_bus{}. In
> >> the CA0132 patch, we would set this flag, which would then be read in
> >> hda_intel.c,azx_pcm_trigger(). Either spin_lock_irqxxx() or the current
> >> spin_lock() would then be called depending on the flag condition.
> >> What is your opinion on an approach like this? Would this be acceptable?
>
> >No. The fact that you are calling the PCM functions is already
> >wrong. The irqsave() is just a side-effect by that, and we can forget
> >about it completely now.
>
> >The only question is where to implement the DSP loader stuff and how.
>
> Ok, we understand. Given that the codec cannot call into the controller's
> PCM interface to load the DSP image, is there an acceptable way you could
> recommend for us to add this functionality to the module, without breaking
> the architecture?
Well, as mentioned earlier, we need to push it up to the controller
layer, i.e. hda_intel.c. For example, add a new op for firmware DMA
transfer in hda_bus_ops.
Could you describe briefly the procedure for the DMA transfer of
CA0132? The code looks too complex to follow.
thanks,
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-07-31 15:15 ` Takashi Iwai
@ 2012-08-01 2:38 ` Ian Minett
2012-08-01 5:48 ` Takashi Iwai
0 siblings, 1 reply; 21+ messages in thread
From: Ian Minett @ 2012-08-01 2:38 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
>>>The only question is where to implement the DSP loader stuff and how.
>>
>> Ok, we understand. Given that the codec cannot call into the controller's
>> PCM interface to load the DSP image, is there an acceptable way you could
>> recommend for us to add this functionality to the module, without breaking
>> the architecture?
>
>Well, as mentioned earlier, we need to push it up to the controller
>layer, i.e. hda_intel.c. For example, add a new op for firmware DMA
>transfer in hda_bus_ops.
>
>Could you describe briefly the procedure for the DMA transfer of
>CA0132? The code looks too complex to follow.
Sure - the DSP code download procedure is:
1. The CA0132 driver extracts DSP data segments that need to download to
the DSP chip.
2. Each of these data segments will be downloaded to the DSP chip via
HDA-link, like audio data.
3. The HDA controller and CA0132 are programmed to transfer the DSP data
segments.
4. The HDA controller FIFO must be flushed on every data segment to ensure
that there is no previous residual data.
These are the controller ops that the CA0132 driver used for the DMA transfer:
#define azx_pcm_open(a) (a->ops->open(a))
#define azx_pcm_close(a) (a->ops->close(a))
#define azx_pcm_prepare(a) (a->ops->prepare(a))
#define azx_pcm_trigger(a, b) (a->ops->trigger(a, b))
#define azx_pcm_hw_free(a) (a->ops->hw_free(a))
Thanks,
Ian
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-08-01 2:38 ` Ian Minett
@ 2012-08-01 5:48 ` Takashi Iwai
2012-08-04 3:29 ` Ian Minett
0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-08-01 5:48 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Tue, 31 Jul 2012 19:38:05 -0700,
Ian Minett wrote:
>
>
>
>
>
>
>
> >>>The only question is where to implement the DSP loader stuff and how.
> >>
> >> Ok, we understand. Given that the codec cannot call into the controller's
> >> PCM interface to load the DSP image, is there an acceptable way you could
> >> recommend for us to add this functionality to the module, without breaking
> >> the architecture?
> >
> >Well, as mentioned earlier, we need to push it up to the controller
> >layer, i.e. hda_intel.c. For example, add a new op for firmware DMA
> >transfer in hda_bus_ops.
> >
> >Could you describe briefly the procedure for the DMA transfer of
> >CA0132? The code looks too complex to follow.
>
> Sure - the DSP code download procedure is:
>
> 1. The CA0132 driver extracts DSP data segments that need to download to
> the DSP chip.
> 2. Each of these data segments will be downloaded to the DSP chip via
> HDA-link, like audio data.
> 3. The HDA controller and CA0132 are programmed to transfer the DSP data
> segments.
So, the data transfer here is just like the normal PCM streaming?
For example, if you have an op like
int (*load_dsp)(struct hda_bus *bus, void *buffer, int size);
and calling this for each segment would work?
(In addition, we'd need to give some way to determine the stop
condition in the codec side.)
> 4. The HDA controller FIFO must be flushed on every data segment to ensure
> that there is no previous residual data.
Actually FIFO flush might be needed for the audio transfer as well.
thanks,
Takashi
> These are the controller ops that the CA0132 driver used for the DMA transfer:
> #define azx_pcm_open(a) (a->ops->open(a))
> #define azx_pcm_close(a) (a->ops->close(a))
> #define azx_pcm_prepare(a) (a->ops->prepare(a))
> #define azx_pcm_trigger(a, b) (a->ops->trigger(a, b))
> #define azx_pcm_hw_free(a) (a->ops->hw_free(a))
>
> Thanks,
> Ian
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-08-01 5:48 ` Takashi Iwai
@ 2012-08-04 3:29 ` Ian Minett
2012-08-04 7:29 ` Takashi Iwai
0 siblings, 1 reply; 21+ messages in thread
From: Ian Minett @ 2012-08-04 3:29 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
>So, the data transfer here is just like the normal PCM streaming?
>For example, if you have an op like
>
> int (*load_dsp)(struct hda_bus *bus, void *buffer, int size);
>
>and calling this for each segment would work?
>(In addition, we'd need to give some way to determine the stop
> condition in the codec side.)
>
Hi,
Yes - this approach would work. We would also need to set the data
format, and control the stop condition.
For example, by adding the following ops to handle the dsp
download process:
/* format is 16-bit Stream Format Structure as defined in HDA spec.
pcm_prepare() callback in the codec will be called so that codec driver
can obtain the stream tag and format to set up the codec.
*/
int (*load_dsp_prepare) \
(struct hda_bus *bus, unsigned short format, void *buffer, int size);
/* start the transfer. */
int (*load_dsp_start)(struct hda_bus *bus);
/* stop the transfer and flush the FIFO. */
int (*load_dsp_stop)(struct hda_bus *bus);
Thanks,
Ian
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-08-04 3:29 ` Ian Minett
@ 2012-08-04 7:29 ` Takashi Iwai
2012-08-08 0:27 ` Ian Minett
0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-08-04 7:29 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Fri, 3 Aug 2012 20:29:44 -0700,
Ian Minett wrote:
>
> >So, the data transfer here is just like the normal PCM streaming?
> >For example, if you have an op like
> >
> > int (*load_dsp)(struct hda_bus *bus, void *buffer, int size);
> >
> >and calling this for each segment would work?
> >(In addition, we'd need to give some way to determine the stop
> > condition in the codec side.)
> >
>
> Hi,
>
> Yes - this approach would work. We would also need to set the data
> format, and control the stop condition.
>
> For example, by adding the following ops to handle the dsp
> download process:
>
> /* format is 16-bit Stream Format Structure as defined in HDA spec.
> pcm_prepare() callback in the codec will be called so that codec driver
> can obtain the stream tag and format to set up the codec.
> */
> int (*load_dsp_prepare) \
> (struct hda_bus *bus, unsigned short format, void *buffer, int size);
>
> /* start the transfer. */
> int (*load_dsp_start)(struct hda_bus *bus);
>
> /* stop the transfer and flush the FIFO. */
> int (*load_dsp_stop)(struct hda_bus *bus);
Looks good, but I think start and stop can be a single trigger like
PCM op, something like:
int (*load_dsp_trigger)(struct hda_bus *bus, bool start);
The second argument could be a generic int like PCM op, but I don't
think we'd need pause or suspend/resume command for DSP loader :)
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-08-04 7:29 ` Takashi Iwai
@ 2012-08-08 0:27 ` Ian Minett
2012-08-08 7:22 ` Takashi Iwai
0 siblings, 1 reply; 21+ messages in thread
From: Ian Minett @ 2012-08-08 0:27 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
>Looks good, but I think start and stop can be a single trigger like
>PCM op, something like:
>
> int (*load_dsp_trigger)(struct hda_bus *bus, bool start);
>
>The second argument could be a generic int like PCM op, but I don't
>think we'd need pause or suspend/resume command for DSP loader :)
Hi Takashi,
The proposed method for handling the DSP load process looks fine to
us, and we can make a start on adding this to the CA0132 driver now.
We can update patch_ca0132.c, but would you be able to assist us with
the modifications to the hda_intel.c side at all? We think it might
result in a smoother process (and involving fewer submissions :) )
given your familiarity with the low-level architecture in that area.
If not, we'd appreciate any info or pointers you can provide on how
and where the necessary modifications need to be made to hda_intel.c.
Thanks again for all your help with this.
Ian
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features.
2012-08-08 0:27 ` Ian Minett
@ 2012-08-08 7:22 ` Takashi Iwai
[not found] ` <1344665872-15537-1-git-send-email-ian_minett@creativelabs.com>
0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-08-08 7:22 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Tue, 7 Aug 2012 17:27:59 -0700,
Ian Minett wrote:
>
> >Looks good, but I think start and stop can be a single trigger like
> >PCM op, something like:
> >
> > int (*load_dsp_trigger)(struct hda_bus *bus, bool start);
> >
> >The second argument could be a generic int like PCM op, but I don't
> >think we'd need pause or suspend/resume command for DSP loader :)
>
> Hi Takashi,
>
> The proposed method for handling the DSP load process looks fine to
> us, and we can make a start on adding this to the CA0132 driver now.
>
> We can update patch_ca0132.c, but would you be able to assist us with
> the modifications to the hda_intel.c side at all? We think it might
> result in a smoother process (and involving fewer submissions :) )
> given your familiarity with the low-level architecture in that area.
Sure, just send me a draft version of your patch, so that I can work
on it.
> If not, we'd appreciate any info or pointers you can provide on how
> and where the necessary modifications need to be made to hda_intel.c.
You had already some working code but it was calling PCM ops.
When you see hda_intel.c, the necessary streaming operations can be
much easier implemented there instead of indirect call of PCM ops, if
you have a dedicated load_dsp_prepare and load_dsp_trigger.
The current azx_pcm_trigger(), for example, looks fairly complex, but
it's simply because of the support for linked PCM streams. If you can
assume a single stream, it's just a call of azx_stream_start() and
azx_stream_stop().
BTW, what would be the license of the firmware?
For upstreaming the firmware, you'd need to clarify its license.
You can see the license texts in kernel-firmware tree / package about
what other vendors give.
thanks,
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP loader code to CA0132 codec
[not found] ` <1344665872-15537-1-git-send-email-ian_minett@creativelabs.com>
@ 2012-08-11 6:17 ` Ian Minett
2012-08-11 7:12 ` Takashi Iwai
2012-08-11 7:19 ` [PATCHv2.1-CA0132 HDA Codec 1/2] Add DSP loader code to CA0132 codec Takashi Iwai
1 sibling, 1 reply; 21+ messages in thread
From: Ian Minett @ 2012-08-11 6:17 UTC (permalink / raw)
To: patch; +Cc: alsa-devel, Ian Minett
From: Ian Minett <ian_minett@creativelabs.com>
Draft patch to add new DSP loader code, with calls to load_dsp_xxxx() functions.
Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 825f5b4..45dce9c 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -1952,22 +1952,13 @@ struct dma_engine {
unsigned short m_converter_format;
void *m_buffer_addr;
unsigned int m_buffer_size;
- unsigned int m_req_size;
- struct snd_pcm_substream *substream;
};
enum dma_state {
- DMA_STATE_RESET = 0,
- DMA_STATE_STOP = 1,
- DMA_STATE_RUN = 2
+ DMA_STATE_STOP = 0,
+ DMA_STATE_RUN = 1
};
-#define azx_pcm_open(a) (a->ops->open(a))
-#define azx_pcm_close(a) (a->ops->close(a))
-#define azx_pcm_prepare(a) (a->ops->prepare(a))
-#define azx_pcm_trigger(a, b) (a->ops->trigger(a, b))
-#define azx_pcm_hw_free(a) (a->ops->hw_free(a))
-
static int dma_convert_to_hda_format(
struct hda_stream_format *stream_format,
unsigned short *hda_format)
@@ -1986,113 +1977,6 @@ static int dma_convert_to_hda_format(
return 0;
}
-static int dma_init(
- struct hda_codec *codec,
- struct dma_engine **pp_dma,
- struct hda_stream_format *stream_format,
- unsigned short *format,
- unsigned int req_size)
-{
- struct dma_engine *dma;
- struct snd_pcm_substream *substream;
- struct snd_pcm *pcm;
- struct snd_pcm_runtime *runtime;
- unsigned int bits;
- snd_pcm_uframes_t frames;
-
- *pp_dma = NULL;
- dma = kzalloc(sizeof(*dma), GFP_KERNEL);
- memset((void *)dma, 0, sizeof(*dma));
-
- dma_convert_to_hda_format(stream_format, format);
- dma->m_converter_format = *format;
-
- dma->substream = NULL;
- pcm = codec->pcm_info->pcm;
- for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- substream; substream = substream->next) {
- if (codec->pcm_info->pcm_type == HDA_PCM_TYPE_SPDIF)
- continue;
-
- if (!SUBSTREAM_BUSY(substream)) {
- dma->substream = substream;
- break;
- }
- }
-
- if (NULL == dma->substream) {
- kfree(dma);
- return -1;
- }
-
- runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
- memset((void *)runtime, 0, sizeof(*runtime));
- dma->substream->runtime = runtime;
- dma->substream->private_data = pcm->private_data;
-
- azx_pcm_open(dma->substream);
- req_size = req_size * 2;
- snd_pcm_lib_malloc_pages(dma->substream, req_size);
-
- runtime->rate = stream_format->sample_rate;
- runtime->channels = stream_format->number_channels;
- runtime->format = SNDRV_PCM_FORMAT_S32_LE;
- runtime->no_period_wakeup = 1;
-
- bits = snd_pcm_format_physical_width(runtime->format);
- runtime->sample_bits = bits;
- bits *= runtime->channels;
- runtime->frame_bits = bits;
- frames = 1;
- while (bits % 8 != 0) {
- bits *= 2;
- frames *= 2;
- }
- runtime->byte_align = bits / 8;
- runtime->min_align = frames;
- runtime->buffer_size = bytes_to_frames(runtime, req_size);
- runtime->period_size = runtime->buffer_size;
- dma->m_req_size = req_size;
- dma->codec = codec;
-
- *pp_dma = dma;
- CA0132_LOG("dma_init: succeeded.\n");
- return 0;
-}
-
-static int dma_prepare(struct dma_engine *dma)
-{
- struct snd_pcm_runtime *runtime;
- int err;
-
- CA0132_LOG("dma_prepare: begin\n");
- runtime = dma->substream->runtime;
-
- err = azx_pcm_prepare(dma->substream);
- if (err < 0)
- return -1;
-
- dma->m_buffer_size = snd_pcm_lib_buffer_bytes(dma->substream);
- dma->m_buffer_addr = runtime->dma_area;
-
- return 0;
-}
-
-static int dma_reset(struct dma_engine *dma)
-{
- struct snd_pcm_runtime *runtime = dma->substream->runtime;
-
- CA0132_LOG("dma_reset: begin\n");
- azx_pcm_hw_free(dma->substream);
- snd_pcm_lib_malloc_pages(dma->substream, dma->m_req_size);
-
- azx_pcm_prepare(dma->substream);
- dma->m_buffer_size = snd_pcm_lib_buffer_bytes(dma->substream);
- dma->m_buffer_addr = runtime->dma_area;
-
- return 0;
-}
-
static int dma_set_state(struct dma_engine *dma, enum dma_state state)
{
int cmd;
@@ -2100,9 +1984,6 @@ static int dma_set_state(struct dma_engine *dma, enum dma_state state)
CA0132_LOG("dma_set_state state=%d\n", state);
switch (state) {
- case DMA_STATE_RESET:
- dma_reset(dma);
- return 0;
case DMA_STATE_STOP:
cmd = SNDRV_PCM_TRIGGER_STOP;
break;
@@ -2113,9 +1994,7 @@ static int dma_set_state(struct dma_engine *dma, enum dma_state state)
return 0;
}
- azx_pcm_trigger(dma->substream, cmd);
-
- return 0;
+ return load_dsp_trigger(dma->codec->bus, cmd);
}
static unsigned int dma_get_buffer_size(struct dma_engine *dma)
@@ -2128,15 +2007,6 @@ static unsigned int *dma_get_buffer_addr(struct dma_engine *dma)
return dma->m_buffer_addr;
}
-static int dma_free_buffer(struct dma_engine *dma)
-{
- azx_pcm_hw_free(dma->substream);
- azx_pcm_close(dma->substream);
- kfree(dma->substream->runtime);
- dma->substream->runtime = NULL;
- return 0;
-}
-
static int dma_xfer(struct dma_engine *dma,
const unsigned int *data,
unsigned int count)
@@ -2160,13 +2030,6 @@ static unsigned int dma_get_stream_id(struct dma_engine *dma)
return spec->dsp_stream_id;
}
-static int dma_exit(struct dma_engine *dma)
-{
- CA0132_LOG("dma_exit\n");
- kfree(dma);
- return 0;
-}
-
/*
* CA0132 chip DSP image segment stuffs
*/
@@ -2384,7 +2247,6 @@ static int dspxfr_one_seg(struct hda_codec *codec,
}
CA0132_DSP_LOG("+++++ DMA complete");
dma_set_state(dma_engine, DMA_STATE_STOP);
- dma_set_state(dma_engine, DMA_STATE_RESET);
}
CTASSERT(run_size_words <= words_to_write);
@@ -2413,17 +2275,29 @@ static int dspxfr_image(struct hda_codec *codec,
struct dma_engine *dma_engine;
unsigned int dma_chan;
unsigned int port_map_mask;
- unsigned int buf_size;
CTASSERT(fls_data != NULL);
if (fls_data == NULL)
return -1;
- buf_size = ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
- status = dma_init(codec, &dma_engine, format, &hda_format, buf_size);
-
- if (FAILED(status))
- return -1;
+ dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
+ if (!dma_engine) {
+ status = -ENOMEM;
+ goto exit;
+ }
+ memset((void*)dma_engine, 0, sizeof(*dma_engine));
+
+ dma_engine->codec = codec;
+ dma_convert_to_hda_format(format, &hda_format);
+ dma_engine->m_converter_format = hda_format;
+ dma_engine->m_buffer_size =
+ ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
+ dma_engine->m_buffer_addr = kzalloc(dma_engine->m_buffer_size,
+ GFP_KERNEL);
+ if (!dma_engine->m_buffer_addr) {
+ status = -ENOMEM;
+ goto exit;
+ }
dma_chan = 0;
do {
@@ -2434,7 +2308,10 @@ static int dspxfr_image(struct hda_codec *codec,
break;
}
- status = dma_prepare(dma_engine);
+ status = load_dsp_prepare(codec->bus,
+ dma_engine->m_converter_format,
+ dma_engine->m_buffer_addr,
+ dma_engine->m_buffer_size);
if (FAILED(status))
break;
@@ -2495,15 +2372,9 @@ static int dspxfr_image(struct hda_codec *codec,
if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
status = dspio_free_dma_chan(codec, dma_chan);
- status = dma_set_state(dma_engine, DMA_STATE_RESET);
- if (FAILED(status))
- status = FAIL_MSG(status, "dma set state Reset fail");
-
- status = dma_free_buffer(dma_engine);
- if (FAILED(status))
- status = FAIL_MSG(status, "dma free buffer fail");
-
- dma_exit(dma_engine);
+exit:
+ kfree(dma_engine->m_buffer_addr);
+ kfree(dma_engine);
return status;
}
--
1.7.4.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP loader code to CA0132 codec
2012-08-11 6:17 ` [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP loader code to CA0132 codec Ian Minett
@ 2012-08-11 7:12 ` Takashi Iwai
2012-08-13 23:04 ` Ian Minett
2012-08-15 5:50 ` [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2 Ian Minett
0 siblings, 2 replies; 21+ messages in thread
From: Takashi Iwai @ 2012-08-11 7:12 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Fri, 10 Aug 2012 23:17:52 -0700,
Ian Minett wrote:
>
> From: Ian Minett <ian_minett@creativelabs.com>
>
> Draft patch to add new DSP loader code, with calls to load_dsp_xxxx() functions.
>
>
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
(snip)
> @@ -2113,9 +1994,7 @@ static int dma_set_state(struct dma_engine *dma, enum dma_state state)
> return 0;
> }
>
> - azx_pcm_trigger(dma->substream, cmd);
> -
> - return 0;
> + return load_dsp_trigger(dma->codec->bus, cmd);
Maybe we just need to pass a boolean to start/stop.
> static unsigned int dma_get_buffer_size(struct dma_engine *dma)
> @@ -2128,15 +2007,6 @@ static unsigned int *dma_get_buffer_addr(struct dma_engine *dma)
> return dma->m_buffer_addr;
> }
>
> -static int dma_free_buffer(struct dma_engine *dma)
> -{
> - azx_pcm_hw_free(dma->substream);
> - azx_pcm_close(dma->substream);
> - kfree(dma->substream->runtime);
> - dma->substream->runtime = NULL;
> - return 0;
OK, I forgot that a cleanup callback is also required...
> + dma_engine->codec = codec;
> + dma_convert_to_hda_format(format, &hda_format);
> + dma_engine->m_converter_format = hda_format;
> + dma_engine->m_buffer_size =
> + ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
> + dma_engine->m_buffer_addr = kzalloc(dma_engine->m_buffer_size,
> + GFP_KERNEL);
Allocating the DMA transfer buffer via kmalloc doesn't work.
The buffer must be aligned and coherent. Thus it's better to let the
buffer management in hda_intel.c.
In the end, what we need in hda_intel.c are:
- a function to allocate buffer, setup BDL and DMA, return the pointer
- a function to start/stop transfer
- a function to clean up DMA, release the buffer
Thus the hda_bus_ops will gain the following ops:
/* bus operators */
struct hda_bus_ops {
...
#ifdef CONFIG_SND_HDA_LOAD_DSP
/* prepare DSP transfer */
void *(*load_dsp_prepare)(struct hda_codec *codec, unsigned int format,
unsigned int byte_size);
/* start/stop DSP transfer */
void (*load_dsp_trigger)(struct hda_codec *codec, bool start);
/* clean up DSP transfer */
void (*load_dsp_cleanup)(struct hda_codec *codec, void *buf,
unsigned int byte_size);
#endif
};
Then define the function like
static inline void *
snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
unsigned int size)
{
return codec->bus->ops.load_dsp_prepare(codec, format, size);
}
and so on.
A remaining question is:
- what is the condition to operate DSP transfer? Must all PCM streams
be closed and streams are released?
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2.1-CA0132 HDA Codec 1/2] Add DSP loader code to CA0132 codec
[not found] ` <1344665872-15537-1-git-send-email-ian_minett@creativelabs.com>
2012-08-11 6:17 ` [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP loader code to CA0132 codec Ian Minett
@ 2012-08-11 7:19 ` Takashi Iwai
1 sibling, 0 replies; 21+ messages in thread
From: Takashi Iwai @ 2012-08-11 7:19 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Fri, 10 Aug 2012 23:17:51 -0700,
Ian Minett wrote:
>
> From: Ian Minett <ian_minett@creativelabs.com>
>
> Hi Takashi,
> Great, it's good to hear that you are able to assist with adding the DSP
> loader modifications to hda_intel.c.
>
> I've attached a draft patch as you requested. Please let us know if you have
> any questions concerning it. The first patch is the previous update to add DSP
> features to patch_ca0132, then the second is the draft patch, to add the
> new DMA loader calls.
Well, for cleanness, I think we should start rather from scratch:
- The first patch to add a new hda_bus_ops for DSP transfer;
it won't touch patch_ca0132.c but only the core part (hda_intel.c,
hda_codec.[ch])
- Add the DSP transfer to patch_ca0132.c;
at this point, just do DMA transfer but no new mixers etc won't be
added
- Add new features to patch_ca0132.c
At the early development stage, the first two items can be a single
patch. We can split to two when applied to the upstream. But, mixing
all changes in a single patch is pretty bad.
So the point is to concentrate only on DMA transfer stuff at first.
Once after confirmed it's working, continue to the new DSP features.
> We're also currently ascertaining the license information to go along with the
> firmware binary release.
Good. This is really mandatory for upstreaming.
> The project team would like to know, beyond the outstanding items mentioned
> above, do you see any other potential issues with the updates to the codec that
> could possibly slow the submission process? We're starting to get rather close
> to our target date.
> Also, once the codec has made it into the ALSA tree, how long do you estimate
> it would take before it is upstreamed to the Linux kernel? If you could
> provide any info on the timeframe for this process, we'd greatly appreciate it.
Heh, this depends on how hard we work :)
If it can be quickly done, it'll be merged even in 3.7 kernel.
Takashi
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP loader code to CA0132 codec
2012-08-11 7:12 ` Takashi Iwai
@ 2012-08-13 23:04 ` Ian Minett
2012-08-15 5:50 ` [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2 Ian Minett
1 sibling, 0 replies; 21+ messages in thread
From: Ian Minett @ 2012-08-13 23:04 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
[-- Attachment #1.1: Type: text/plain, Size: 5710 bytes --]
>So the point is to concentrate only on DMA transfer stuff at first.
>Once after confirmed it's working, continue to the new DSP features.,
Ok, that makes sense.
>A remaining question is:
>- what is the condition to operate DSP transfer? Must all PCM streams
> be closed and streams are released?
Yes, all PCM streams need to be closed and released for the DSP transfer.
Since the driver loads the DSP during the init phase, there shouldn't be
any active streams at that time.
- Ian
Notice
The information in this message is confidential and may be legally privileged. It is intended
solely for the addressee. Access to this message by anyone else is unauthorized. If you are not
the intended recipient, any disclosure, copying or distribution of the message, or any action taken
by you in reliance on it, is prohibited and may be unlawful. This email is for communication
purposes only. It is not intended to constitute an offer or form a binding agreement. Our company
accepts no liability for the content of this email, or for the consequences of any actions taken on
the basis of the information provided. If you have received this message in error, please delete
it and contact the sender immediately. Thank you.
Takashi Iwai
<tiwai@suse.de>
To
08/11/2012 12:12 AM Ian Minett <ian_minett@creativelabs.com>
cc
alsa-devel@alsa-project.org
Subject
Re: [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP
loader code to CA0132 codec
At Fri, 10 Aug 2012 23:17:52 -0700,
Ian Minett wrote:
>
> From: Ian Minett <ian_minett@creativelabs.com>
>
> Draft patch to add new DSP loader code, with calls to load_dsp_xxxx() functions.
>
>
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
(snip)
> @@ -2113,9 +1994,7 @@ static int dma_set_state(struct dma_engine *dma, enum dma_state state)
> return 0;
> }
>
> - azx_pcm_trigger(dma->substream, cmd);
> -
> - return 0;
> + return load_dsp_trigger(dma->codec->bus, cmd);
Maybe we just need to pass a boolean to start/stop.
> static unsigned int dma_get_buffer_size(struct dma_engine *dma)
> @@ -2128,15 +2007,6 @@ static unsigned int *dma_get_buffer_addr(struct dma_engine *dma)
> return dma->m_buffer_addr;
> }
>
> -static int dma_free_buffer(struct dma_engine *dma)
> -{
> - azx_pcm_hw_free(dma->substream);
> - azx_pcm_close(dma->substream);
> - kfree(dma->substream->runtime);
> - dma->substream->runtime = NULL;
> - return 0;
OK, I forgot that a cleanup callback is also required...
> + dma_engine->codec = codec;
> + dma_convert_to_hda_format(format, &hda_format);
> + dma_engine->m_converter_format = hda_format;
> + dma_engine->m_buffer_size =
> + ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
> + dma_engine->m_buffer_addr = kzalloc(dma_engine->m_buffer_size,
> + GFP_KERNEL);
Allocating the DMA transfer buffer via kmalloc doesn't work.
The buffer must be aligned and coherent. Thus it's better to let the
buffer management in hda_intel.c.
In the end, what we need in hda_intel.c are:
- a function to allocate buffer, setup BDL and DMA, return the pointer
- a function to start/stop transfer
- a function to clean up DMA, release the buffer
Thus the hda_bus_ops will gain the following ops:
/* bus operators */
struct hda_bus_ops {
...
#ifdef CONFIG_SND_HDA_LOAD_DSP
/* prepare DSP transfer */
void *(*load_dsp_prepare)(struct hda_codec *codec, unsigned int format,
unsigned int byte_size);
/* start/stop DSP transfer */
void (*load_dsp_trigger)(struct hda_codec *codec, bool start);
/* clean up DSP transfer */
void (*load_dsp_cleanup)(struct hda_codec *codec, void *buf,
unsigned int byte_size);
#endif
};
Then define the function like
static inline void *
snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
unsigned int size)
{
return codec->bus->ops.load_dsp_prepare(codec, format, size);
}
and so on.
A remaining question is:
- what is the condition to operate DSP transfer? Must all PCM streams
be closed and streams are released?
Takashi
ForwardSourceID:NT0001D94A
[-- Attachment #1.2: graycol.gif --]
[-- Type: image/gif, Size: 105 bytes --]
[-- Attachment #1.3: pic16413.gif --]
[-- Type: image/gif, Size: 1255 bytes --]
[-- Attachment #1.4: ecblank.gif --]
[-- Type: image/gif, Size: 45 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2
2012-08-11 7:12 ` Takashi Iwai
2012-08-13 23:04 ` Ian Minett
@ 2012-08-15 5:50 ` Ian Minett
2012-08-15 7:06 ` Takashi Iwai
1 sibling, 1 reply; 21+ messages in thread
From: Ian Minett @ 2012-08-15 5:50 UTC (permalink / raw)
To: patch; +Cc: alsa-devel, Ian Minett
From: Ian Minett <ian_minett@creativelabs.com>
Thanks for the recent feedback - based on that we've updated the recent
draft patch for adding the DSP loader to patch_ca0132.c :
- move DMA buffer management out (to be handled in hda_intel)
- add call to cleanup DMA
- change start/stop flag to boolean
If the changes look acceptable, do we have enough for us to get started on
adding the DSP loader updates to patch_ca0132 (working with the 'from scratch'
CA0132 as you suggested)?
Please let us know if you can think of anything else that needs to be worked
out, or anything we need to provide for the bus op mods.
Thanks very much,
Ian
Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 45dce9c..0d07445 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -1979,22 +1979,22 @@ static int dma_convert_to_hda_format(
static int dma_set_state(struct dma_engine *dma, enum dma_state state)
{
- int cmd;
+ bool cmd;
CA0132_LOG("dma_set_state state=%d\n", state);
switch (state) {
case DMA_STATE_STOP:
- cmd = SNDRV_PCM_TRIGGER_STOP;
+ cmd = false;
break;
case DMA_STATE_RUN:
- cmd = SNDRV_PCM_TRIGGER_START;
+ cmd = true;
break;
default:
return 0;
}
- return load_dsp_trigger(dma->codec->bus, cmd);
+ return snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
}
static unsigned int dma_get_buffer_size(struct dma_engine *dma)
@@ -2281,10 +2281,8 @@ static int dspxfr_image(struct hda_codec *codec,
return -1;
dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
- if (!dma_engine) {
- status = -ENOMEM;
- goto exit;
- }
+ if (!dma_engine)
+ return -ENOMEM;
memset((void*)dma_engine, 0, sizeof(*dma_engine));
dma_engine->codec = codec;
@@ -2292,12 +2290,6 @@ static int dspxfr_image(struct hda_codec *codec,
dma_engine->m_converter_format = hda_format;
dma_engine->m_buffer_size =
ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
- dma_engine->m_buffer_addr = kzalloc(dma_engine->m_buffer_size,
- GFP_KERNEL);
- if (!dma_engine->m_buffer_addr) {
- status = -ENOMEM;
- goto exit;
- }
dma_chan = 0;
do {
@@ -2308,11 +2300,11 @@ static int dspxfr_image(struct hda_codec *codec,
break;
}
- status = load_dsp_prepare(codec->bus,
- dma_engine->m_converter_format,
- dma_engine->m_buffer_addr,
- dma_engine->m_buffer_size);
- if (FAILED(status))
+ dma_engine->m_buffer_addr =
+ snd_hda_codec_load_dsp_prepare(codec,
+ dma_engine->m_converter_format,
+ dma_engine->m_buffer_size);
+ if (!dma_engine->m_buffer_addr)
break;
if (ovly) {
@@ -2372,8 +2364,9 @@ static int dspxfr_image(struct hda_codec *codec,
if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
status = dspio_free_dma_chan(codec, dma_chan);
-exit:
- kfree(dma_engine->m_buffer_addr);
+ snd_hda_codec_load_dsp_cleanup(codec,
+ dma_engine->m_buffer_addr,
+ dma_engine->m_buffer_size);
kfree(dma_engine);
return status;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2
2012-08-15 5:50 ` [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2 Ian Minett
@ 2012-08-15 7:06 ` Takashi Iwai
2012-08-22 1:36 ` Ian Minett
0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2012-08-15 7:06 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
At Tue, 14 Aug 2012 22:50:06 -0700,
Ian Minett wrote:
>
> From: Ian Minett <ian_minett@creativelabs.com>
>
> Thanks for the recent feedback - based on that we've updated the recent
> draft patch for adding the DSP loader to patch_ca0132.c :
>
> - move DMA buffer management out (to be handled in hda_intel)
> - add call to cleanup DMA
> - change start/stop flag to boolean
>
> If the changes look acceptable, do we have enough for us to get started on
> adding the DSP loader updates to patch_ca0132 (working with the 'from scratch'
> CA0132 as you suggested)?
>
> Please let us know if you can think of anything else that needs to be worked
> out, or anything we need to provide for the bus op mods.
Any missing thing will be revealed later once when you start coding
the DSP loader part in hda_intel.c :)
So now, as I suggested, start writing a code just loading the firmware
DSP code based on the _bare_ upstream tree. That is, don't base on
the already modified patch_ca0132.c or hda_intel.c. Write a patch
that can be applied on the top of the current Linus tree, for example.
There shouldn't be any addition of controls, etc, at this point.
Just do load the firmware. This is the very first start.
I can help debugging or give review / advise. But obviously you are
at the better position start writing it since you have the test
hardware and the firmware data, both of which are missing for other
developers (including me).
Once when a draft patch is ready or you face to a problem, let me
know.
thanks,
Takashi
>
> Thanks very much,
> Ian
>
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
>
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index 45dce9c..0d07445 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -1979,22 +1979,22 @@ static int dma_convert_to_hda_format(
>
> static int dma_set_state(struct dma_engine *dma, enum dma_state state)
> {
> - int cmd;
> + bool cmd;
>
> CA0132_LOG("dma_set_state state=%d\n", state);
>
> switch (state) {
> case DMA_STATE_STOP:
> - cmd = SNDRV_PCM_TRIGGER_STOP;
> + cmd = false;
> break;
> case DMA_STATE_RUN:
> - cmd = SNDRV_PCM_TRIGGER_START;
> + cmd = true;
> break;
> default:
> return 0;
> }
>
> - return load_dsp_trigger(dma->codec->bus, cmd);
> + return snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
> }
>
> static unsigned int dma_get_buffer_size(struct dma_engine *dma)
> @@ -2281,10 +2281,8 @@ static int dspxfr_image(struct hda_codec *codec,
> return -1;
>
> dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
> - if (!dma_engine) {
> - status = -ENOMEM;
> - goto exit;
> - }
> + if (!dma_engine)
> + return -ENOMEM;
> memset((void*)dma_engine, 0, sizeof(*dma_engine));
>
> dma_engine->codec = codec;
> @@ -2292,12 +2290,6 @@ static int dspxfr_image(struct hda_codec *codec,
> dma_engine->m_converter_format = hda_format;
> dma_engine->m_buffer_size =
> ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT;
> - dma_engine->m_buffer_addr = kzalloc(dma_engine->m_buffer_size,
> - GFP_KERNEL);
> - if (!dma_engine->m_buffer_addr) {
> - status = -ENOMEM;
> - goto exit;
> - }
>
> dma_chan = 0;
> do {
> @@ -2308,11 +2300,11 @@ static int dspxfr_image(struct hda_codec *codec,
> break;
> }
>
> - status = load_dsp_prepare(codec->bus,
> - dma_engine->m_converter_format,
> - dma_engine->m_buffer_addr,
> - dma_engine->m_buffer_size);
> - if (FAILED(status))
> + dma_engine->m_buffer_addr =
> + snd_hda_codec_load_dsp_prepare(codec,
> + dma_engine->m_converter_format,
> + dma_engine->m_buffer_size);
> + if (!dma_engine->m_buffer_addr)
> break;
>
> if (ovly) {
> @@ -2372,8 +2364,9 @@ static int dspxfr_image(struct hda_codec *codec,
> if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
> status = dspio_free_dma_chan(codec, dma_chan);
>
> -exit:
> - kfree(dma_engine->m_buffer_addr);
> + snd_hda_codec_load_dsp_cleanup(codec,
> + dma_engine->m_buffer_addr,
> + dma_engine->m_buffer_size);
> kfree(dma_engine);
>
> return status;
> --
> 1.7.4.1
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2
2012-08-15 7:06 ` Takashi Iwai
@ 2012-08-22 1:36 ` Ian Minett
2012-08-22 13:39 ` Takashi Iwai
0 siblings, 1 reply; 21+ messages in thread
From: Ian Minett @ 2012-08-22 1:36 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
>There shouldn't be any addition of controls, etc, at this point.
>Just do load the firmware. This is the very first start.
>
>I can help debugging or give review / advise. But obviously you are
>at the better position start writing it since you have the test
>hardware and the firmware data, both of which are missing for other
>developers (including me).
>
>Once when a draft patch is ready or you face to a problem, let me
>know.
Ok, thanks.
We do seem to be having problems in the DSP load prepare function:
void *(*load_dsp_prepare)(struct hda_codec *codec, unsigned int format,
unsigned int byte_size);
This is the place where the buffer gets allocated, and BDL and DMA set up, and the
pointer returned.
Is there any sample code or other info you could provide, to give us a better idea
of how the function and these actions should be implemented?
Thanks very much,
- Ian
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2
2012-08-22 1:36 ` Ian Minett
@ 2012-08-22 13:39 ` Takashi Iwai
0 siblings, 0 replies; 21+ messages in thread
From: Takashi Iwai @ 2012-08-22 13:39 UTC (permalink / raw)
To: Ian Minett; +Cc: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 2179 bytes --]
At Tue, 21 Aug 2012 18:36:01 -0700,
Ian Minett wrote:
>
>
> >There shouldn't be any addition of controls, etc, at this point.
> >Just do load the firmware. This is the very first start.
> >
> >I can help debugging or give review / advise. But obviously you are
> >at the better position start writing it since you have the test
> >hardware and the firmware data, both of which are missing for other
> >developers (including me).
> >
> >Once when a draft patch is ready or you face to a problem, let me
> >know.
>
> Ok, thanks.
> We do seem to be having problems in the DSP load prepare function:
>
> void *(*load_dsp_prepare)(struct hda_codec *codec, unsigned int format,
> unsigned int byte_size);
>
> This is the place where the buffer gets allocated, and BDL and DMA set up, and the
> pointer returned.
> Is there any sample code or other info you could provide, to give us a better idea
> of how the function and these actions should be implemented?
Maybe better to start from a bit clean up of SG buffer handling code
in general. The first patch below makes the sg-buffer helpers more
generic so that they can be called even without sg-buffer support.
Then, setup_bdle() in hda_intel.c can be changed cleanly by passing
struct snd_dma_buffer pointer, as found in the second patch.
The rest of the second patch is the additions of callbacks supposedly
needed for your case.
load_dsp_prepare() callback takes parameters of the format value
(encoded by snd_hda_calc_stream_format()), the byte size of the buffer
to allocate, and a pointer to struct snd_dma_buffer. It'll return the
stream tag in success, or a negative error code. The allocated buffer
is stored in snd_dma_buffer.area pointer. It'll be an SG-buffer, but
can be accessed linearly by that pointer.
Then, the caller should set up the codec by itself. Unlike PCM
prepare, it doesn't set up the codec, as the DSP loading might need
more special setups.
After setting up these and the buffer is filled, call
load_dsp_trigger() callback. It's a simple start/stop of the
controller.
Then call load_dsp_clean() callback. The buffer will be released
there, too.
Takashi
[-- Attachment #2: 0001-ALSA-Make-snd_sgbuf_get_-ptr-addr-available-for-non-.patch --]
[-- Type: application/octet-stream, Size: 6939 bytes --]
>From c7b5d707a25f57c8bc38a9d7b7a61b8db45d37e1 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Wed, 22 Aug 2012 14:36:32 +0200
Subject: [PATCH] ALSA: Make snd_sgbuf_get_{ptr|addr}() available for non-SG
cases
Passing struct snd_dma_buffer pointer instead, so that they work no
matter whether real SG buffer is used or not.
This is a preliminary work for the HD-audio DSP loader code.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/sound/memalloc.h | 27 +++++++++++++++++++++++++--
include/sound/pcm.h | 39 ++++++++++++++-------------------------
sound/core/pcm_memory.c | 26 --------------------------
sound/core/sgbuf.c | 28 ++++++++++++++++++++++++++++
4 files changed, 67 insertions(+), 53 deletions(-)
diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index c425062..9201520 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -98,8 +98,10 @@ static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
/*
* return the physical address at the corresponding offset
*/
-static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
{
+ struct snd_sg_buf *sgbuf = dmab->private_data;
dma_addr_t addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
addr &= PAGE_MASK;
return addr + offset % PAGE_SIZE;
@@ -108,10 +110,31 @@ static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t off
/*
* return the virtual address at the corresponding offset
*/
-static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
+static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
+ size_t offset)
{
+ struct snd_sg_buf *sgbuf = dmab->private_data;
return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
}
+
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size);
+#else
+/* non-SG versions */
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return dmab->addr + offset;
+}
+
+static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return dmab->area + offset;
+}
+
+#define snd_sgbuf_get_chunk_size(dmab, ofs, size) (size)
+
#endif /* CONFIG_SND_DMA_SGBUF */
/* allocate/release a buffer */
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index cdca2ab..0c054b9 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -982,53 +982,42 @@ static int snd_pcm_lib_alloc_vmalloc_32_buffer
_snd_pcm_lib_alloc_vmalloc_buffer \
(subs, size, GFP_KERNEL | GFP_DMA32 | __GFP_ZERO)
+#define snd_pcm_get_dma_buf(substream) ((substream)->runtime->dma_buffer_p)
+
#ifdef CONFIG_SND_DMA_SGBUF
/*
* SG-buffer handling
*/
#define snd_pcm_substream_sgbuf(substream) \
- ((substream)->runtime->dma_buffer_p->private_data)
-
-static inline dma_addr_t
-snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- return snd_sgbuf_get_addr(sg, ofs);
-}
-
-static inline void *
-snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- return snd_sgbuf_get_ptr(sg, ofs);
-}
+ snd_pcm_get_dma_buf(substream)->private_data
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
unsigned long offset);
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
- unsigned int ofs, unsigned int size);
-
#else /* !SND_DMA_SGBUF */
/*
* fake using a continuous buffer
*/
+#define snd_pcm_sgbuf_ops_page NULL
+#endif /* SND_DMA_SGBUF */
+
static inline dma_addr_t
snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
{
- return substream->runtime->dma_addr + ofs;
+ return snd_sgbuf_get_addr(snd_pcm_get_dma_buf(substream), ofs);
}
static inline void *
snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
{
- return substream->runtime->dma_area + ofs;
+ return snd_sgbuf_get_ptr(snd_pcm_get_dma_buf(substream), ofs);
}
-#define snd_pcm_sgbuf_ops_page NULL
-
-#define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size) (size)
-
-#endif /* SND_DMA_SGBUF */
+static inline unsigned int
+snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
+ unsigned int ofs, unsigned int size)
+{
+ return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size);
+}
/* handle mmap counter - PCM mmap callback should handle this counter properly */
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 9571313..69e01c4 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -327,32 +327,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
}
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-
-/*
- * compute the max chunk size with continuous pages on sg-buffer
- */
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
- unsigned int ofs, unsigned int size)
-{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- unsigned int start, end, pg;
-
- start = ofs >> PAGE_SHIFT;
- end = (ofs + size - 1) >> PAGE_SHIFT;
- /* check page continuity */
- pg = sg->table[start].addr >> PAGE_SHIFT;
- for (;;) {
- start++;
- if (start > end)
- break;
- pg++;
- if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
- return (start << PAGE_SHIFT) - ofs;
- }
- /* ok, all on continuous pages */
- return size;
-}
-EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
#endif /* CONFIG_SND_DMA_SGBUF */
/**
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index d0f0035..da3c619 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/memalloc.h>
@@ -136,3 +137,30 @@ void *snd_malloc_sgbuf_pages(struct device *device,
snd_free_sgbuf_pages(dmab); /* free the table */
return NULL;
}
+
+
+/*
+ * compute the max chunk size with continuous pages on sg-buffer
+ */
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ struct snd_sg_buf *sg = dmab->private_data;
+ unsigned int start, end, pg;
+
+ start = ofs >> PAGE_SHIFT;
+ end = (ofs + size - 1) >> PAGE_SHIFT;
+ /* check page continuity */
+ pg = sg->table[start].addr >> PAGE_SHIFT;
+ for (;;) {
+ start++;
+ if (start > end)
+ break;
+ pg++;
+ if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
+ return (start << PAGE_SHIFT) - ofs;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
--
1.7.11.4
[-- Attachment #3: hda-dsp-load-callbacks.diff --]
[-- Type: application/octet-stream, Size: 4375 bytes --]
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 209bea4..bbf5d81 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1351,7 +1351,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
* set up a BDL entry
*/
static int setup_bdle(struct azx *chip,
- struct snd_pcm_substream *substream,
+ struct snd_dma_buffer *dmab,
struct azx_dev *azx_dev, u32 **bdlp,
int ofs, int size, int with_ioc)
{
@@ -1364,12 +1364,12 @@ static int setup_bdle(struct azx *chip,
if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
return -EINVAL;
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ 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_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ 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);
@@ -1428,7 +1428,8 @@ static int azx_setup_periods(struct azx *chip,
bdl_pos_adj[chip->dev_index]);
pos_adj = 0;
} else {
- ofs = setup_bdle(chip, substream, azx_dev,
+ ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+ azx_dev,
&bdl, ofs, pos_adj,
!substream->runtime->no_period_wakeup);
if (ofs < 0)
@@ -1438,10 +1439,12 @@ static int azx_setup_periods(struct azx *chip,
pos_adj = 0;
for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
+ ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+ azx_dev, &bdl, ofs,
period_bytes - pos_adj, 0);
else
- ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
+ ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+ azx_dev, &bdl, ofs,
period_bytes,
!substream->runtime->no_period_wakeup);
if (ofs < 0)
@@ -2398,6 +2401,93 @@ static void azx_stop_chip(struct azx *chip)
chip->initialized = 0;
}
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/*
+ * DSP loading code (e.g. for CA0132)
+ */
+
+/* use the first stream for loading DSP */
+static struct azx_dev *
+azx_get_dsp_loader_dev(struct azx *chip)
+{
+ return &chip->azx_dev[chip->playback_index_offset];
+}
+
+static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp)
+{
+ u32 *bdl;
+ struct azx *chip = bus->private_data;
+ struct azx_dev *azx_dev;
+ int err;
+
+ if (snd_hda_lock_devices(bus))
+ return -EBUSY;
+
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ byte_size, bufp);
+ if (err < 0)
+ goto error;
+
+ azx_dev = azx_get_dsp_loader_dev(chip);
+ 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(azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(azx_dev, SD_BDLPU, 0);
+
+ azx_dev->frags = 0;
+ bdl = (u32 *)azx_dev->bdl.area;
+ err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0);
+ if (err < 0)
+ goto error;
+
+ azx_setup_controller(chip, azx_dev);
+ return azx_dev->stream_tag;
+
+ error:
+ snd_hda_unlock_devices(bus);
+ return err;
+}
+
+static void azx_load_dsp_trigger(struct hda_bus *bus, bool start)
+{
+ struct azx *chip = bus->private_data;
+ 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;
+}
+
+static void azx_load_dsp_cleanup(struct hda_bus *bus,
+ struct snd_dma_buffer *dmab)
+{
+ struct azx *chip = bus->private_data;
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+ /* reset BDL address */
+ azx_sd_writel(azx_dev, SD_BDLPL, 0);
+ azx_sd_writel(azx_dev, SD_BDLPU, 0);
+ azx_sd_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+
+ snd_dma_free_pages(dmab);
+
+ snd_hda_unlock_devices(bus);
+}
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* power-up/down the controller */
static void azx_power_notify(struct hda_bus *bus)
[-- Attachment #4: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply related [flat|nested] 21+ messages in thread
end of thread, other threads:[~2012-08-22 13:39 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1342223070-25852-1-git-send-email-ian_minett@creativelabs.com>
2012-07-13 23:44 ` [PATCH - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore() in azx_pcm_trigger(). This prevents deadlock when an interrupt occurs, caused by chip->reg_lock contention ian_minett
2012-07-16 10:18 ` [PATCH - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features Takashi Iwai
2012-07-25 18:01 ` [PATCHv2 " Ian Minett
2012-07-25 18:01 ` [PATCHv2 - CA0132 HDA Codec 2/2] Change spin_lock()/unlock() to spin_lock_irqsave()/restore() Ian Minett
2012-07-26 8:06 ` [PATCHv2 - CA0132 HDA Codec 1/2] ALSA: Update Creative CA0132 codec to add DSP features Takashi Iwai
[not found] ` <OF0CFC3DA9.A9100453-ON88257A48.006BF087-88257A48.006FCB42@cli.creative.com>
2012-07-28 5:57 ` Takashi Iwai
[not found] ` <OF301523C9.1A0D8E05-ON88257A4B.0074BC06-88257A4B.007D5630@cli.creative.com>
2012-07-31 15:15 ` Takashi Iwai
2012-08-01 2:38 ` Ian Minett
2012-08-01 5:48 ` Takashi Iwai
2012-08-04 3:29 ` Ian Minett
2012-08-04 7:29 ` Takashi Iwai
2012-08-08 0:27 ` Ian Minett
2012-08-08 7:22 ` Takashi Iwai
[not found] ` <1344665872-15537-1-git-send-email-ian_minett@creativelabs.com>
2012-08-11 6:17 ` [PATCHv2.1-CA0132 HDA Codec 2/2] Add DSP loader code to CA0132 codec Ian Minett
2012-08-11 7:12 ` Takashi Iwai
2012-08-13 23:04 ` Ian Minett
2012-08-15 5:50 ` [PATCHv2.1-CA0132 HDA Codec 1/1] Draft DSP loader update #2 Ian Minett
2012-08-15 7:06 ` Takashi Iwai
2012-08-22 1:36 ` Ian Minett
2012-08-22 13:39 ` Takashi Iwai
2012-08-11 7:19 ` [PATCHv2.1-CA0132 HDA Codec 1/2] Add DSP loader code to CA0132 codec Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).