[alsa-devel] [PATCH 03/11] drivers: char: add AXD Audio Processing IP driver
Qais Yousef
qais.yousef at imgtec.com
Tue Oct 28 12:26:21 CET 2014
AXD is Audio Processing IP by Imagination Technologies that can
perform decoding, encoding, equalisation, resampling, mixing,
synchronisation and audio playback.
this patch adds defs and initialisation files
Signed-off-by: Qais Yousef <qais.yousef at imgtec.com>
Cc: Arnd Bergmann <arnd at arndb.de>
Cc: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
Cc: Grant Likely <grant.likely at linaro.org>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: <devicetree at vger.kernel.org>
Cc: <alsa-devel at alsa-project.org>
---
drivers/char/axd/axd_api.h | 641 +++++++++++++++++++++++++
drivers/char/axd/axd_module.c | 1064 +++++++++++++++++++++++++++++++++++++++++
drivers/char/axd/axd_module.h | 99 ++++
include/linux/axd.h | 32 ++
4 files changed, 1836 insertions(+)
create mode 100644 drivers/char/axd/axd_api.h
create mode 100644 drivers/char/axd/axd_module.c
create mode 100644 drivers/char/axd/axd_module.h
create mode 100644 include/linux/axd.h
diff --git a/drivers/char/axd/axd_api.h b/drivers/char/axd/axd_api.h
new file mode 100644
index 000000000000..0d732f173f55
--- /dev/null
+++ b/drivers/char/axd/axd_api.h
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2011-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * Main API to the AXD for access from the host.
+ */
+#ifndef AXD_API_H_
+#define AXD_API_H_
+
+#include <linux/types.h>
+
+
+#define THREAD_COUNT 4
+#define AXD_MAX_PIPES 3
+
+
+#define AXD_DESCRIPTOR_READY_BIT 0x80000000
+#define AXD_DESCRIPTOR_INUSE_BIT 0x40000000
+#define AXD_DESCRIPTOR_EOS_BIT 0x20000000
+#define AXD_DESCRIPTOR_SIZE_MASK 0x0000FFFF
+
+struct axd_buffer_desc {
+ uint32_t status_size;
+ uint32_t data_ptr;
+ uint32_t pts_high;
+ uint32_t pts_low;
+};
+
+#define AXD_INPUT_DESCRIPTORS 10
+struct axd_input {
+ struct axd_buffer_desc descriptors[AXD_INPUT_DESCRIPTORS];
+};
+
+#define AXD_OUTPUT_DESCRIPTORS 10
+struct axd_output {
+ struct axd_buffer_desc descriptors[AXD_OUTPUT_DESCRIPTORS];
+};
+
+struct axd_ctrlbuf_item {
+ uint32_t reg;
+ uint32_t val;
+};
+
+/**
+ * struct axd_memory_map - axd memory mapped region
+ * @kick: kick register holds the type of kick to process
+ * @int_status: interrupt status register
+ * @int_mask: interrupt mask register
+ * @in_kick_count: array of number of input kicks to process
+ * @in_int_count: array of number of input interrupts to process
+ * @out_kick_count: array of number of output kicks to process
+ * @out_int_count: array of number of output interrupts to process
+ * @control_command: this register contains the command type to process
+ * @control_data: this register contains the command data to process
+ * @pc: starting pc value of each hardware thread
+ * @error: last error value
+ * @gic_irq: which gic irqs to use for host and axd in this format:
+ * host_gic_irq[31:16]:axd_gic_irq[15:0]
+ * @freq: count/compare clock frequency in MHz
+ * @input: array of struct axd_input which holds the descriptors
+ * @output: array of struct axd_output which holds the descriptors
+ * @ctrlbuf_size: size of control buffer used to group multiple
+ * configurations changes into a single request
+ * @ctrlbuf_ctrl: position of ctrlbuf requests
+ * @ctrlbuf: the actual control buffer used to group requests
+ * size of which is defined by the firmware
+ */
+struct axd_memory_map {
+ uint32_t kick;
+ uint32_t int_status;
+ uint32_t int_mask;
+ uint32_t in_kick_count[AXD_MAX_PIPES];
+ uint32_t in_int_count[AXD_MAX_PIPES];
+ uint32_t out_kick_count[AXD_MAX_PIPES];
+ uint32_t out_int_count[AXD_MAX_PIPES];
+ uint32_t control_command;
+ uint32_t control_data;
+ uint32_t pc[THREAD_COUNT];
+ uint32_t error;
+ uint32_t gic_irq;
+ uint32_t freq;
+ uint32_t reserved01[0x04];
+ struct axd_input input[AXD_MAX_PIPES];
+ struct axd_output output[AXD_MAX_PIPES];
+ uint32_t reserved02[40];
+ uint32_t reserved03[12];
+ uint32_t ctrlbuf_size;
+ uint32_t ctrlbuf_ctrl;
+ struct axd_ctrlbuf_item ctrlbuf[];
+};
+
+#define AXD_ANY_KICK_BIT 0x80000000
+#define AXD_KICK_MASK 0x0000000F
+#define AXD_KICK_CTRL_BIT 0x00000001
+#define AXD_KICK_DATA_IN_BIT 0x00000002
+#define AXD_KICK_DATA_OUT_BIT 0x00000004
+
+#define AXD_INT_KICK_DONE 0x00000001
+#define AXD_INT_DATAIN 0x00000002
+#define AXD_INT_DATAOUT 0x00000004
+#define AXD_INT_CTRL 0x00000008
+#define AXD_INT_ERROR 0x00000010
+
+enum axd_ctrl_cmd {
+ AXD_CTRL_CMD_NONE = 0,
+ AXD_CTRL_CMD_BUSY,
+ AXD_CTRL_CMD_READY,
+ AXD_CTRL_CMD_FLUSH,
+ AXD_CTRL_CMD_RESET_BD,
+ AXD_CTRL_CMD_RESET_PIPE,
+ AXD_CTRL_CMD_CTRLBUF_FLUSH,
+ AXD_CTRL_CMD_READ_REGISTER = 0x80000000, /* lower 16bits are address */
+ AXD_CTRL_CMD_WRITE_REGISTER = 0xC0000000, /* lower 16bits are address */
+};
+
+struct axd_hdr {
+ uint32_t axd_magic;
+ uint32_t hdr_size;
+ uint32_t thread_pc[THREAD_COUNT];
+ uint32_t cmd_block_offset;
+ uint32_t cmd_block_size;
+ char build_str[64];
+ uint32_t log_offset;
+};
+
+/* Register I/F */
+#define AXD_REG_VERSION 0x0000
+#define AXD_REG_CONFIG0 0x0004
+#define AXD_REG_CONFIG1 0x0008
+#define AXD_REG_CONFIG2 0x000C
+#define AXD_REG_CONFIG3 0x0010
+#define AXD_REG_BUFFER_BASE 0x0014
+#define AXD_REG_DEBUG_MASK 0x0018
+/* 0x1c reserved */
+#define AXD_REG_INPUT0_CONTROL 0x0020
+#define AXD_REG_INPUT0_GAIN 0x0024
+#define AXD_REG_INPUT0_UPMIX 0x0028
+#define AXD_REG_INPUT1_CONTROL 0x0030
+#define AXD_REG_INPUT1_GAIN 0x0034
+#define AXD_REG_INPUT1_UPMIX 0x0038
+#define AXD_REG_INPUT2_CONTROL 0x0040
+#define AXD_REG_INPUT2_GAIN 0x0044
+#define AXD_REG_INPUT2_UPMIX 0x0048
+#define AXD_REG_INPUT0_MUTE 0x0050
+#define AXD_REG_INPUT1_MUTE 0x0054
+#define AXD_REG_INPUT2_MUTE 0x0058
+#define AXD_REG_MIXER_CONTROL 0x0080
+#define AXD_REG_EQ_CTRL_GAIN 0x0084
+#define AXD_REG_EQ_BAND0 0x0088
+#define AXD_REG_EQ_BAND1 0x008C
+#define AXD_REG_EQ_BAND2 0x0090
+#define AXD_REG_EQ_BAND3 0x0094
+#define AXD_REG_EQ_BAND4 0x0098
+#define AXD_REG_MUX0 0x00B0
+#define AXD_REG_MUX1 0x00B4
+#define AXD_REG_MUX2 0x00B8
+#define AXD_REG_OUTPUT0_CONTROL 0x00D0
+#define AXD_REG_OUTPUT0_DOWNMIX 0x00D4
+#define AXD_REG_OUTPUT0_EQCTRL 0x00D8
+#define AXD_REG_OUTPUT0_EQBAND0 0x00DC
+#define AXD_REG_OUTPUT0_EQBAND1 0x00E0
+#define AXD_REG_OUTPUT0_EQBAND2 0x00E4
+#define AXD_REG_OUTPUT0_EQBAND3 0x00E8
+#define AXD_REG_OUTPUT0_EQBAND4 0x00EC
+#define AXD_REG_OUTPUT1_CONTROL 0x00F0
+#define AXD_REG_OUTPUT1_DOWNMIX 0x00F4
+#define AXD_REG_OUTPUT1_EQCTRL 0x00F8
+#define AXD_REG_OUTPUT1_EQBAND0 0x00FC
+#define AXD_REG_OUTPUT1_EQBAND1 0x0100
+#define AXD_REG_OUTPUT1_EQBAND2 0x0104
+#define AXD_REG_OUTPUT1_EQBAND3 0x0108
+#define AXD_REG_OUTPUT1_EQBAND4 0x010C
+#define AXD_REG_OUTPUT2_CONTROL 0x0110
+#define AXD_REG_OUTPUT2_DOWNMIX 0x0114
+#define AXD_REG_OUTPUT2_EQCTRL 0x0118
+#define AXD_REG_OUTPUT2_EQBAND0 0x011C
+#define AXD_REG_OUTPUT2_EQBAND1 0x0120
+#define AXD_REG_OUTPUT2_EQBAND2 0x0124
+#define AXD_REG_OUTPUT2_EQBAND3 0x0128
+#define AXD_REG_OUTPUT2_EQBAND4 0x012c
+#define AXD_REG_DEC0_AAC_VERSION 0x0200
+#define AXD_REG_DEC0_AAC_CHANNELS 0x0204
+#define AXD_REG_DEC0_AAC_PROFILE 0x0208
+#define AXD_REG_DEC0_AAC_STREAM_TYPE 0x020C
+#define AXD_REG_DEC0_AAC_SAMPLERATE 0x0210
+#define AXD_REG_DEC1_AAC_VERSION 0x0220
+#define AXD_REG_DEC1_AAC_CHANNELS 0x0224
+#define AXD_REG_DEC1_AAC_PROFILE 0x0228
+#define AXD_REG_DEC1_AAC_STREAM_TYPE 0x022C
+#define AXD_REG_DEC1_AAC_SAMPLERATE 0x0230
+#define AXD_REG_DEC2_AAC_VERSION 0x0240
+#define AXD_REG_DEC2_AAC_CHANNELS 0x0244
+#define AXD_REG_DEC2_AAC_PROFILE 0x0248
+#define AXD_REG_DEC2_AAC_STREAM_TYPE 0x024C
+#define AXD_REG_DEC2_AAC_SAMPLERATE 0x0250
+#define AXD_REG_DEC0_COOK_FLAVOUR 0x0260
+#define AXD_REG_DEC1_COOK_FLAVOUR 0x0264
+#define AXD_REG_DEC2_COOK_FLAVOUR 0x0268
+#define AXD_REG_DEC0_FLAC_CHANNELS 0x0270
+#define AXD_REG_DEC0_FLAC_SAMPLERATE 0x0274
+#define AXD_REG_DEC0_FLAC_BITS_PER_SAMPLE 0x0278
+#define AXD_REG_DEC0_FLAC_MD5_CHECKING 0x027C
+#define AXD_REG_DEC1_FLAC_CHANNELS 0x0280
+#define AXD_REG_DEC1_FLAC_SAMPLERATE 0x0284
+#define AXD_REG_DEC1_FLAC_BITS_PER_SAMPLE 0x0288
+#define AXD_REG_DEC1_FLAC_MD5_CHECKING 0x028C
+#define AXD_REG_DEC2_FLAC_CHANNELS 0x0290
+#define AXD_REG_DEC2_FLAC_SAMPLERATE 0x0294
+#define AXD_REG_DEC2_FLAC_BITS_PER_SAMPLE 0x0298
+#define AXD_REG_DEC2_FLAC_MD5_CHECKING 0x029C
+#define AXD_REG_DEC0_MPEG_CHANNELS 0x02A0
+#define AXD_REG_DEC0_MPEG_MLCHANNEL 0x02A4
+#define AXD_REG_DEC1_MPEG_CHANNELS 0x02A8
+#define AXD_REG_DEC1_MPEG_MLCHANNEL 0x02AC
+#define AXD_REG_DEC2_MPEG_CHANNELS 0x02B0
+#define AXD_REG_DEC2_MPEG_MLCHANNEL 0x02B4
+#define AXD_REG_DEC0_WMA_PLAYER_OPT 0x02D0
+#define AXD_REG_DEC0_WMA_DRC_SETTING 0x02D4
+#define AXD_REG_DEC0_WMA_PEAK_AMP_REF 0x02D8
+#define AXD_REG_DEC0_WMA_RMS_AMP_REF 0x02DC
+#define AXD_REG_DEC0_WMA_PEAK_AMP_TARGET 0x02E0
+#define AXD_REG_DEC0_WMA_RMS_AMP_TARGET 0x02E4
+#define AXD_REG_DEC0_WMA_PCM_VAL_BITS_PER_SAMPLE 0x02F4
+#define AXD_REG_DEC0_WMA_PCM_CONTAINER_SIZE 0x02F8
+#define AXD_REG_DEC0_WMA_WMA_FORMAT_TAG 0x02FC
+#define AXD_REG_DEC0_WMA_WMA_CHANNELS 0x0300
+#define AXD_REG_DEC0_WMA_WMA_SAMPLES_PER_SEC 0x0304
+#define AXD_REG_DEC0_WMA_WMA_AVG_BYTES_PER_SEC 0x0308
+#define AXD_REG_DEC0_WMA_WMA_BLOCK_ALIGN 0x030C
+#define AXD_REG_DEC0_WMA_WMA_VAL_BITS_PER_SAMPLE 0x0310
+#define AXD_REG_DEC0_WMA_WMA_CHANNEL_MASK 0x0314
+#define AXD_REG_DEC0_WMA_WMA_ENCODE_OPTS 0x0318
+#define AXD_REG_DEC1_WMA_PLAYER_OPT 0x0320
+#define AXD_REG_DEC1_WMA_DRC_SETTING 0x0324
+#define AXD_REG_DEC1_WMA_PEAK_AMP_REF 0x0328
+#define AXD_REG_DEC1_WMA_RMS_AMP_REF 0x032C
+#define AXD_REG_DEC1_WMA_PEAK_AMP_TARGET 0x0330
+#define AXD_REG_DEC1_WMA_RMS_AMP_TARGET 0x0334
+#define AXD_REG_DEC1_WMA_PCM_VAL_BITS_PER_SAMPLE 0x0344
+#define AXD_REG_DEC1_WMA_PCM_CONTAINER_SIZE 0x0348
+#define AXD_REG_DEC1_WMA_WMA_FORMAT_TAG 0x034C
+#define AXD_REG_DEC1_WMA_WMA_CHANNELS 0x0350
+#define AXD_REG_DEC1_WMA_WMA_SAMPLES_PER_SEC 0x0354
+#define AXD_REG_DEC1_WMA_WMA_AVG_BYTES_PER_SEC 0x0358
+#define AXD_REG_DEC1_WMA_WMA_BLOCK_ALIGN 0x035C
+#define AXD_REG_DEC1_WMA_WMA_VAL_BITS_PER_SAMPLE 0x0360
+#define AXD_REG_DEC1_WMA_WMA_CHANNEL_MASK 0x0364
+#define AXD_REG_DEC1_WMA_WMA_ENCODE_OPTS 0x0368
+#define AXD_REG_DEC2_WMA_PLAYER_OPT 0x0370
+#define AXD_REG_DEC2_WMA_DRC_SETTING 0x0374
+#define AXD_REG_DEC2_WMA_PEAK_AMP_REF 0x0378
+#define AXD_REG_DEC2_WMA_RMS_AMP_REF 0x037C
+#define AXD_REG_DEC2_WMA_PEAK_AMP_TARGET 0x0380
+#define AXD_REG_DEC2_WMA_RMS_AMP_TARGET 0x0384
+#define AXD_REG_DEC2_WMA_PCM_VAL_BITS_PER_SAMPLE 0x0394
+#define AXD_REG_DEC2_WMA_PCM_CONTAINER_SIZE 0x0398
+#define AXD_REG_DEC2_WMA_WMA_FORMAT_TAG 0x039C
+#define AXD_REG_DEC2_WMA_WMA_CHANNELS 0x03A0
+#define AXD_REG_DEC2_WMA_WMA_SAMPLES_PER_SEC 0x03A4
+#define AXD_REG_DEC2_WMA_WMA_AVG_BYTES_PER_SEC 0x03A8
+#define AXD_REG_DEC2_WMA_WMA_BLOCK_ALIGN 0x03AC
+#define AXD_REG_DEC2_WMA_WMA_VAL_BITS_PER_SAMPLE 0x03B0
+#define AXD_REG_DEC2_WMA_WMA_CHANNEL_MASK 0x03B4
+#define AXD_REG_DEC2_WMA_WMA_ENCODE_OPTS 0x03B8
+#define AXD_REG_PCMIN0_SAMPLE_RATE 0x3C0
+#define AXD_REG_PCMIN0_CHANNELS 0x3C4
+#define AXD_REG_PCMIN0_BITS_PER_SAMPLE 0x3C8
+#define AXD_REG_PCMIN0_JUSTIFICATION 0x3CC
+#define AXD_REG_PCMIN1_SAMPLE_RATE 0x3D0
+#define AXD_REG_PCMIN1_CHANNELS 0x3D4
+#define AXD_REG_PCMIN1_BITS_PER_SAMPLE 0x3D8
+#define AXD_REG_PCMIN1_JUSTIFICATION 0x3DC
+#define AXD_REG_PCMIN2_SAMPLE_RATE 0x3E0
+#define AXD_REG_PCMIN2_CHANNELS 0x3E4
+#define AXD_REG_PCMIN2_BITS_PER_SAMPLE 0x3E8
+#define AXD_REG_PCMIN2_JUSTIFICATION 0x3EC
+#define AXD_REG_PCMOUT0_BITS_PER_SAMPLE 0x3F0
+#define AXD_REG_PCMOUT0_JUSTIFICATION 0x3F4
+#define AXD_REG_PCMOUT1_BITS_PER_SAMPLE 0x3F8
+#define AXD_REG_PCMOUT1_JUSTIFICATION 0x3FC
+#define AXD_REG_PCMOUT2_BITS_PER_SAMPLE 0x400
+#define AXD_REG_PCMOUT2_JUSTIFICATION 0x404
+#define AXD_REG_DEC0_AC3_CHANNELS 0x410
+#define AXD_REG_DEC0_AC3_CHANNEL_ORDER 0x414
+#define AXD_REG_DEC0_AC3_MODE 0x418
+#define AXD_REG_DEC1_AC3_CHANNELS 0x420
+#define AXD_REG_DEC1_AC3_CHANNEL_ORDER 0x424
+#define AXD_REG_DEC1_AC3_MODE 0x428
+#define AXD_REG_DEC2_AC3_CHANNELS 0x430
+#define AXD_REG_DEC2_AC3_CHANNEL_ORDER 0x434
+#define AXD_REG_DEC2_AC3_MODE 0x438
+#define AXD_REG_DEC0_DDPLUS_CONFIG 0x440
+#define AXD_REG_DEC0_DDPLUS_CHANNEL_ORDER 0x444
+#define AXD_REG_DEC1_DDPLUS_CONFIG 0x448
+#define AXD_REG_DEC1_DDPLUS_CHANNEL_ORDER 0x44C
+#define AXD_REG_DEC2_DDPLUS_CONFIG 0x450
+#define AXD_REG_DEC2_DDPLUS_CHANNEL_ORDER 0x454
+#define AXD_REG_EQ_OUT0_POWER_B0_C0_C3 0x460
+#define AXD_REG_EQ_OUT0_POWER_B0_C4_C7 0x464
+#define AXD_REG_EQ_OUT0_POWER_B1_C0_C3 0x468
+#define AXD_REG_EQ_OUT0_POWER_B1_C4_C7 0x46C
+#define AXD_REG_EQ_OUT0_POWER_B2_C0_C3 0x470
+#define AXD_REG_EQ_OUT0_POWER_B2_C4_C7 0x474
+#define AXD_REG_EQ_OUT0_POWER_B3_C0_C3 0x478
+#define AXD_REG_EQ_OUT0_POWER_B3_C4_C7 0x47C
+#define AXD_REG_EQ_OUT0_POWER_B4_C0_C3 0x480
+#define AXD_REG_EQ_OUT0_POWER_B4_C4_C7 0x484
+#define AXD_REG_EQ_OUT1_POWER_B0_C0_C3 0x488
+#define AXD_REG_EQ_OUT1_POWER_B0_C4_C7 0x48C
+#define AXD_REG_EQ_OUT1_POWER_B1_C0_C3 0x490
+#define AXD_REG_EQ_OUT1_POWER_B1_C4_C7 0x494
+#define AXD_REG_EQ_OUT1_POWER_B2_C0_C3 0x498
+#define AXD_REG_EQ_OUT1_POWER_B2_C4_C7 0x49C
+#define AXD_REG_EQ_OUT1_POWER_B3_C0_C3 0x4A0
+#define AXD_REG_EQ_OUT1_POWER_B3_C4_C7 0x4A4
+#define AXD_REG_EQ_OUT1_POWER_B4_C0_C3 0x4A8
+#define AXD_REG_EQ_OUT1_POWER_B4_C4_C7 0x4AC
+#define AXD_REG_EQ_OUT2_POWER_B0_C0_C3 0x4B0
+#define AXD_REG_EQ_OUT2_POWER_B0_C4_C7 0x4B4
+#define AXD_REG_EQ_OUT2_POWER_B1_C0_C3 0x4B8
+#define AXD_REG_EQ_OUT2_POWER_B1_C4_C7 0x4BC
+#define AXD_REG_EQ_OUT2_POWER_B2_C0_C3 0x4C0
+#define AXD_REG_EQ_OUT2_POWER_B2_C4_C7 0x4C4
+#define AXD_REG_EQ_OUT2_POWER_B3_C0_C3 0x4C8
+#define AXD_REG_EQ_OUT2_POWER_B3_C4_C7 0x4CC
+#define AXD_REG_EQ_OUT2_POWER_B4_C0_C3 0x4D0
+#define AXD_REG_EQ_OUT2_POWER_B4_C4_C7 0x4D4
+#define AXD_REG_RESAMPLER0_FIN 0x4E0
+#define AXD_REG_RESAMPLER0_FOUT 0x4E4
+#define AXD_REG_RESAMPLER1_FIN 0x4E8
+#define AXD_REG_RESAMPLER1_FOUT 0x4EC
+#define AXD_REG_RESAMPLER2_FIN 0x4F0
+#define AXD_REG_RESAMPLER2_FOUT 0x4f4
+#define AXD_REG_DEC0_ALAC_CHANNELS 0x500
+#define AXD_REG_DEC0_ALAC_DEPTH 0x504
+#define AXD_REG_DEC0_ALAC_SAMPLE_RATE 0x508
+#define AXD_REG_DEC0_ALAC_FRAME_LENGTH 0x50C
+#define AXD_REG_DEC0_ALAC_MAX_FRAME_BYTES 0x510
+#define AXD_REG_DEC0_ALAC_AVG_BIT_RATE 0x514
+#define AXD_REG_DEC1_ALAC_CHANNELS 0x520
+#define AXD_REG_DEC1_ALAC_DEPTH 0x524
+#define AXD_REG_DEC1_ALAC_SAMPLE_RATE 0x528
+#define AXD_REG_DEC1_ALAC_FRAME_LENGTH 0x52C
+#define AXD_REG_DEC1_ALAC_MAX_FRAME_BYTES 0x530
+#define AXD_REG_DEC1_ALAC_AVG_BIT_RATE 0x534
+#define AXD_REG_DEC2_ALAC_CHANNELS 0x540
+#define AXD_REG_DEC2_ALAC_DEPTH 0x544
+#define AXD_REG_DEC2_ALAC_SAMPLE_RATE 0x548
+#define AXD_REG_DEC2_ALAC_FRAME_LENGTH 0x54C
+#define AXD_REG_DEC2_ALAC_MAX_FRAME_BYTES 0x550
+#define AXD_REG_DEC2_ALAC_AVG_BIT_RATE 0x554
+/* 0x558 to 0x55C reserved */
+#define AXD_REG_ENC0_FLAC_CHANNELS 0x560
+#define AXD_REG_ENC0_FLAC_BITS_PER_SAMPLE 0x564
+#define AXD_REG_ENC0_FLAC_SAMPLE_RATE 0x568
+#define AXD_REG_ENC0_FLAC_TOTAL_SAMPLES 0x56C
+#define AXD_REG_ENC0_FLAC_DO_MID_SIDE_STEREO 0x570
+#define AXD_REG_ENC0_FLAC_LOOSE_MID_SIDE_STEREO 0x574
+#define AXD_REG_ENC0_FLAC_DO_EXHAUSTIVE_MODEL_SEARCH 0x578
+#define AXD_REG_ENC0_FLAC_MIN_RESIDUAL_PARTITION_ORDER 0x57C
+#define AXD_REG_ENC0_FLAC_MAX_RESIDUAL_PARTITION_ORDER 0x580
+#define AXD_REG_ENC0_FLAC_BLOCK_SIZE 0x584
+#define AXD_REG_ENC0_FLAC_BYTE_COUNT 0x588
+#define AXD_REG_ENC0_FLAC_SAMPLE_COUNT 0x58C
+#define AXD_REG_ENC0_FLAC_FRAME_COUNT 0x590
+#define AXD_REG_ENC0_FLAC_FRAME_BYTES 0x594
+/* 0x598 to 0x59C reserved */
+#define AXD_REG_ENC1_FLAC_CHANNELS 0x5A0
+#define AXD_REG_ENC1_FLAC_BITS_PER_SAMPLE 0x5A4
+#define AXD_REG_ENC1_FLAC_SAMPLE_RATE 0x5A8
+#define AXD_REG_ENC1_FLAC_TOTAL_SAMPLES 0x5AC
+#define AXD_REG_ENC1_FLAC_DO_MID_SIDE_STEREO 0x5B0
+#define AXD_REG_ENC1_FLAC_LOOSE_MID_SIDE_STEREO 0x5B4
+#define AXD_REG_ENC1_FLAC_DO_EXHAUSTIVE_MODEL_SEARCH 0x5B8
+#define AXD_REG_ENC1_FLAC_MIN_RESIDUAL_PARTITION_ORDER 0x5BC
+#define AXD_REG_ENC1_FLAC_MAX_RESIDUAL_PARTITION_ORDER 0x5C0
+#define AXD_REG_ENC1_FLAC_BLOCK_SIZE 0x5C4
+#define AXD_REG_ENC1_FLAC_BYTE_COUNT 0x5C8
+#define AXD_REG_ENC1_FLAC_SAMPLE_COUNT 0x5CC
+#define AXD_REG_ENC1_FLAC_FRAME_COUNT 0x5D0
+#define AXD_REG_ENC1_FLAC_FRAME_BYTES 0x5D4
+/* 0x5D8 to 0x5DC reserved */
+#define AXD_REG_ENC2_FLAC_CHANNELS 0x5E0
+#define AXD_REG_ENC2_FLAC_BITS_PER_SAMPLE 0x5E4
+#define AXD_REG_ENC2_FLAC_SAMPLE_RATE 0x5E8
+#define AXD_REG_ENC2_FLAC_TOTAL_SAMPLES 0x5EC
+#define AXD_REG_ENC2_FLAC_DO_MID_SIDE_STEREO 0x5F0
+#define AXD_REG_ENC2_FLAC_LOOSE_MID_SIDE_STEREO 0x5F4
+#define AXD_REG_ENC2_FLAC_DO_EXHAUSTIVE_MODEL_SEARCH 0x5F8
+#define AXD_REG_ENC2_FLAC_MIN_RESIDUAL_PARTITION_ORDER 0x5FC
+#define AXD_REG_ENC2_FLAC_MAX_RESIDUAL_PARTITION_ORDER 0x600
+#define AXD_REG_ENC2_FLAC_BLOCK_SIZE 0x604
+#define AXD_REG_ENC2_FLAC_BYTE_COUNT 0x608
+#define AXD_REG_ENC2_FLAC_SAMPLE_COUNT 0x60C
+#define AXD_REG_ENC2_FLAC_FRAME_COUNT 0x610
+#define AXD_REG_ENC2_FLAC_FRAME_BYTES 0x614
+/* 0x618 to 0x61C reserved */
+#define AXD_REG_ENC0_ALAC_CHANNELS 0x620
+#define AXD_REG_ENC0_ALAC_DEPTH 0x624
+#define AXD_REG_ENC0_ALAC_SAMPLE_RATE 0x628
+#define AXD_REG_ENC0_ALAC_FRAME_LENGTH 0x62C
+#define AXD_REG_ENC0_ALAC_MAX_FRAME_BYTES 0x630
+#define AXD_REG_ENC0_ALAC_AVG_BIT_RATE 0x634
+#define AXD_REG_ENC0_ALAC_FAST_MODE 0x638
+/* 0x63C to 0x64C reserved */
+#define AXD_REG_ENC1_ALAC_CHANNELS 0x650
+#define AXD_REG_ENC1_ALAC_DEPTH 0x654
+#define AXD_REG_ENC1_ALAC_SAMPLE_RATE 0x658
+#define AXD_REG_ENC1_ALAC_FRAME_LENGTH 0x65C
+#define AXD_REG_ENC1_ALAC_MAX_FRAME_BYTES 0x660
+#define AXD_REG_ENC1_ALAC_AVG_BIT_RATE 0x664
+#define AXD_REG_ENC1_ALAC_FAST_MODE 0x668
+/* 0x66C to 0x67C reserved */
+#define AXD_REG_ENC2_ALAC_CHANNELS 0x680
+#define AXD_REG_ENC2_ALAC_DEPTH 0x684
+#define AXD_REG_ENC2_ALAC_SAMPLE_RATE 0x688
+#define AXD_REG_ENC2_ALAC_FRAME_LENGTH 0x68C
+#define AXD_REG_ENC2_ALAC_MAX_FRAME_BYTES 0x690
+#define AXD_REG_ENC2_ALAC_AVG_BIT_RATE 0x694
+#define AXD_REG_ENC2_ALAC_FAST_MODE 0x698
+/* 0x69C to 0x6AC reserved */
+#define AXD_REG_MS11_MODE 0x6B0
+#define AXD_REG_MS11_COMMON_CONFIG0 0x6B4
+#define AXD_REG_MS11_COMMON_CONFIG1 0x6B8
+#define AXD_REG_MS11_DDT_CONFIG0 0x6Bc
+#define AXD_REG_MS11_DDC_CONFIG0 0x6C0
+#define AXD_REG_MS11_EXT_PCM_CONFIG0 0x6C4
+/* 0x6C8 and 0x6CC reserved */
+#define AXD_REG_OUTPUT0_DCPP_CONTROL 0x6D0
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_CONTROL 0x6D4
+#define AXD_REG_OUTPUT0_DCPP_BAND_CONTROL 0x6D8
+#define AXD_REG_OUTPUT0_DCPP_MAX_DELAY_SAMPLES 0x6DC
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_DELAY_SAMPLES 0x6E0
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_SHIFT 0x6E4
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_A0 0x6E8
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_A1 0x6EC
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_A2 0x6F0
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_B0 0x6F4
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_B1 0x6F8
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_SHIFT 0x6FC
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_A0 0x700
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_A1 0x704
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_A2 0x708
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_B0 0x70C
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_B1 0x710
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_OUTPUT_VOLUME 0x714
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_PASSTHROUGH_GAIN 0x718
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_INVERSE_PASSTHROUGH_GAIN 0x71C
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_GAIN 0x720
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_A0 0x724
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_A1 0x728
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_A2 0x72C
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_B0 0x730
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_B1 0x734
+#define AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_SHIFT 0x738
+#define AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_A0 0x73C
+#define AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_A1 0x740
+#define AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_A2 0x744
+#define AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_B0 0x748
+#define AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_B1 0x74C
+/* 0x750 to 0x764 reserved */
+#define AXD_REG_OUTPUT1_DCPP_CONTROL 0x768
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_CONTROL 0x76C
+#define AXD_REG_OUTPUT1_DCPP_BAND_CONTROL 0x770
+#define AXD_REG_OUTPUT1_DCPP_MAX_DELAY_SAMPLES 0x774
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_DELAY_SAMPLES 0x778
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_SHIFT 0x77C
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_A0 0x780
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_A1 0x784
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_A2 0x788
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_B0 0x78C
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_B1 0x790
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_SHIFT 0x794
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_A0 0x798
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_A1 0x79C
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_A2 0x7A0
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_B0 0x7A4
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_B1 0x7A8
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_OUTPUT_VOLUME 0x7AC
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_PASSTHROUGH_GAIN 0x7B0
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_INVERSE_PASSTHROUGH_GAIN 0x7B4
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_GAIN 0x7B8
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_A0 0x7BC
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_A1 0x7C0
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_A2 0x7C4
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_B0 0x7C8
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_B1 0x7CC
+#define AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_SHIFT 0x7D0
+#define AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_A0 0x7D4
+#define AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_A1 0x7D8
+#define AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_A2 0x7DC
+#define AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_B0 0x7E0
+#define AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_B1 0x7E4
+/* 0x7E8 to 0x7FC reserved */
+#define AXD_REG_OUTPUT2_DCPP_CONTROL 0x800
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_CONTROL 0x804
+#define AXD_REG_OUTPUT2_DCPP_BAND_CONTROL 0x808
+#define AXD_REG_OUTPUT2_DCPP_MAX_DELAY_SAMPLES 0x80C
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_DELAY_SAMPLES 0x810
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_SHIFT 0x814
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_A0 0x818
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_A1 0x81C
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_A2 0x820
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_B0 0x824
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_B1 0x828
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_SHIFT 0x82C
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_A0 0x830
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_A1 0x834
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_A2 0x838
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_B0 0x83C
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_B1 0x840
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_OUTPUT_VOLUME 0x844
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_PASSTHROUGH_GAIN 0x848
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_INVERSE_PASSTHROUGH_GAIN 0x84C
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_GAIN 0x850
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_A0 0x854
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_A1 0x858
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_A2 0x85C
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_B0 0x860
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_B1 0x864
+#define AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_SHIFT 0x868
+#define AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_A0 0x86C
+#define AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_A1 0x870
+#define AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_A2 0x874
+#define AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_B0 0x878
+#define AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_B1 0x87C
+/* 0x880 to 0x89C reserved */
+#define AXD_REG_DEC0_SBC_SAMPLE_RATE 0x8A0
+#define AXD_REG_DEC0_SBC_AUDIO_MODE 0x8A4
+#define AXD_REG_DEC0_SBC_BLOCKS 0x8A8
+#define AXD_REG_DEC0_SBC_SUBBANDS 0x8AC
+#define AXD_REG_DEC0_SBC_BITPOOL 0x8B0
+#define AXD_REG_DEC0_SBC_ALLOCATION_MODE 0x8B4
+#define AXD_REG_DEC1_SBC_SAMPLE_RATE 0x8B8
+#define AXD_REG_DEC1_SBC_AUDIO_MODE 0x8BC
+#define AXD_REG_DEC1_SBC_BLOCKS 0x8C0
+#define AXD_REG_DEC1_SBC_SUBBANDS 0x8C4
+#define AXD_REG_DEC1_SBC_BITPOOL 0x8C8
+#define AXD_REG_DEC1_SBC_ALLOCATION_MODE 0x8CC
+#define AXD_REG_DEC2_SBC_SAMPLE_RATE 0x8D0
+#define AXD_REG_DEC2_SBC_AUDIO_MODE 0x8D4
+#define AXD_REG_DEC2_SBC_BLOCKS 0x8D8
+#define AXD_REG_DEC2_SBC_SUBBANDS 0x8DC
+#define AXD_REG_DEC2_SBC_BITPOOL 0x8E0
+#define AXD_REG_DEC2_SBC_ALLOCATION_MODE 0x8E4
+/* 0x8E8 to 0x8EC reserved */
+#define AXD_REG_SYNC_MODE 0x8F0
+/* 0x8F4 to 0x8FC reserved */
+#define AXD_REG_INPUT0_BUFFER_OCCUPANCY 0x900
+#define AXD_REG_INPUT1_BUFFER_OCCUPANCY 0x904
+#define AXD_REG_INPUT2_BUFFER_OCCUPANCY 0x908
+/* 0x90C reserved */
+
+/* Register masks */
+#define AXD_INCTRL_ENABLE_MASK 0x1
+#define AXD_INCTRL_ENABLE_SHIFT 31
+#define AXD_INCTRL_ENABLE_BITS \
+ (AXD_INCTRL_ENABLE_MASK << AXD_INCTRL_ENABLE_SHIFT)
+#define AXD_INCTRL_SOURCE_MASK 0x3
+#define AXD_INCTRL_SOURCE_SHIFT 8
+#define AXD_INCTRL_SOURCE_BITS \
+ (AXD_INCTRL_SOURCE_MASK << AXD_INCTRL_SOURCE_SHIFT)
+#define AXD_INCTRL_CODEC_MASK 0x7FF
+#define AXD_INCTRL_CODEC_SHIFT 0
+#define AXD_INCTRL_CODEC_BITS \
+ (AXD_INCTRL_CODEC_MASK << AXD_INCTRL_CODEC_SHIFT)
+
+#define AXD_OUTCTRL_ENABLE_MASK 0x1
+#define AXD_OUTCTRL_ENABLE_SHIFT 31
+#define AXD_OUTCTRL_ENABLE_BITS \
+ (AXD_OUTCTRL_ENABLE_MASK << AXD_OUTCTRL_ENABLE_SHIFT)
+#define AXD_OUTCTRL_SINK_MASK 0x3
+#define AXD_OUTCTRL_SINK_SHIFT 0
+#define AXD_OUTCTRL_SINK_BITS \
+ (AXD_OUTCTRL_SINK_MASK << AXD_OUTCTRL_SINK_SHIFT)
+#define AXD_OUTCTRL_CODEC_MASK 0xFF
+#define AXD_OUTCTRL_CODEC_SHIFT 2
+#define AXD_OUTCTRL_CODEC_BITS \
+ (AXD_OUTCTRL_CODEC_MASK << AXD_OUTCTRL_CODEC_SHIFT)
+
+#define AXD_EQCTRL_ENABLE_MASK 0x1
+#define AXD_EQCTRL_ENABLE_SHIFT 31
+#define AXD_EQCTRL_ENABLE_BITS \
+ (AXD_EQCTRL_ENABLE_MASK << AXD_EQCTRL_ENABLE_SHIFT)
+#define AXD_EQCTRL_GAIN_MASK 0x7F
+#define AXD_EQCTRL_GAIN_SHIFT 0
+#define AXD_EQCTRL_GAIN_BITS \
+ (AXD_EQCTRL_GAIN_MASK << AXD_EQCTRL_GAIN_SHIFT)
+
+#define AXD_EQBANDX_GAIN_MASK 0xFF
+#define AXD_EQBANDX_GAIN_SHIFT 0
+#define AXD_EQBANDX_GAIN_BITS \
+ (AXD_EQBANDX_GAIN_MASK << AXD_EQBANDX_GAIN_SHIFT)
+
+#define AXD_DCPP_CTRL_ENABLE_MASK 0x1
+#define AXD_DCPP_CTRL_ENABLE_SHIFT 31
+#define AXD_DCPP_CTRL_ENABLE_BITS \
+ (AXD_DCPP_CTRL_ENABLE_MASK << AXD_DCPP_CTRL_ENABLE_SHIFT)
+#define AXD_DCPP_CTRL_CHANNELS_MASK 0xF
+#define AXD_DCPP_CTRL_CHANNELS_SHIFT 27
+#define AXD_DCPP_CTRL_CHANNELS_BITS \
+ (AXD_DCPP_CTRL_CHANNELS_MASK << AXD_DCPP_CTRL_CHANNELS_SHIFT)
+#define AXD_DCPP_CTRL_MODE_MASK 0x1
+#define AXD_DCPP_CTRL_MODE_SHIFT 26
+#define AXD_DCPP_CTRL_MODE_BITS \
+ (AXD_DCPP_CTRL_MODE_MASK << AXD_DCPP_CTRL_MODE_SHIFT)
+#define AXD_DCPP_CTRL_EQ_MODE_MASK 0x1
+#define AXD_DCPP_CTRL_EQ_MODE_SHIFT 25
+#define AXD_DCPP_CTRL_EQ_MODE_BITS \
+ (AXD_DCPP_CTRL_EQ_MODE_MASK << AXD_DCPP_CTRL_EQ_MODE_SHIFT)
+#define AXD_DCPP_CTRL_EQ_BANDS_MASK 0xFF
+#define AXD_DCPP_CTRL_EQ_BANDS_SHIFT 17
+#define AXD_DCPP_CTRL_EQ_BANDS_BITS \
+ (AXD_DCPP_CTRL_EQ_BANDS_MASK << AXD_DCPP_CTRL_EQ_BANDS_SHIFT)
+#define AXD_DCPP_CTRL_SUBBAND_ENABLE_MASK 0x1
+#define AXD_DCPP_CTRL_SUBBAND_ENABLE_SHIFT 16
+#define AXD_DCPP_CTRL_SUBBAND_ENABLE_BITS \
+ (AXD_DCPP_CTRL_SUBBAND_ENABLE_MASK << AXD_DCPP_CTRL_SUBBAND_ENABLE_SHIFT)
+#define AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_MASK 0xFF
+#define AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_SHIFT 8
+#define AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_BITS \
+ (AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_MASK << AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_SHIFT)
+#define AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_MASK 0xFF
+#define AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_SHIFT 0
+#define AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_BITS \
+ (AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_MASK << AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_SHIFT)
+
+#define AXD_DCPP_CHANNEL_CTRL_CHANNEL_MASK 0xFF
+#define AXD_DCPP_CHANNEL_CTRL_CHANNEL_SHIFT 24
+#define AXD_DCPP_CHANNEL_CTRL_CHANNEL_BITS \
+ (AXD_DCPP_CHANNEL_CTRL_CHANNEL_MASK << AXD_DCPP_CHANNEL_CTRL_CHANNEL_SHIFT)
+#define AXD_DCPP_CHANNEL_CTRL_SUBBAND_MASK 0x1
+#define AXD_DCPP_CHANNEL_CTRL_SUBBAND_SHIFT 23
+#define AXD_DCPP_CHANNEL_CTRL_SUBBAND_BITS \
+ (AXD_DCPP_CHANNEL_CTRL_SUBBAND_MASK << AXD_DCPP_CHANNEL_CTRL_SUBBAND_SHIFT)
+
+#endif /* AXD_API_H_ */
diff --git a/drivers/char/axd/axd_module.c b/drivers/char/axd/axd_module.c
new file mode 100644
index 000000000000..690446ffd155
--- /dev/null
+++ b/drivers/char/axd/axd_module.c
@@ -0,0 +1,1064 @@
+/*
+ * Copyright (C) 2011-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * AXD is a hardware IP that provides various audio processing capabilities for
+ * user applications, offloading the core on which the application is running
+ * and saving its valuable MIPS.
+ */
+#include <linux/axd.h>
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kdev_t.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+/* this is required by MIPS ioremap_cachable() */
+#include <asm/pgtable.h>
+
+#include "axd_cmds.h"
+#include "axd_cmds_internal.h"
+#include "axd_hdr.h"
+#include "axd_module.h"
+#include "axd_platform.h"
+#include "axd_sysfs.h"
+#include "axd_ts_driver.h"
+
+#define AXD_NAME "axd"
+
+#define AXD_MGCNUM 0x66445841 /* AXDf */
+#define LZO_MGCNUM 0x4f5a4c89 /* .LZO */
+
+#define AXD_LDFW_RETRIES 400
+
+#define WATCHDOG_TIMEOUT (3*HZ)
+
+/* enums/structs */
+enum axd_devtype {
+ AXD_UNKNOWN = 0,
+ AXD_CTRL,
+ AXD_INPUT,
+ AXD_OUTPUT,
+};
+
+#define SYNC_MGCNUM 0x7FFFFFFF80000000ull
+
+struct axd_sync_data {
+ u64 magic;
+ u64 pts_us;
+};
+
+/* functions start here */
+static int minor_to_devtype(unsigned int minor)
+{
+ if (minor < MAX_CTRL_DEVICES)
+ return AXD_CTRL;
+ else if (minor < (MAX_IN_DEVICES + MAX_CTRL_DEVICES))
+ return AXD_INPUT;
+ else if (minor < MAX_NUM_DEVICES)
+ return AXD_OUTPUT;
+ return AXD_UNKNOWN;
+}
+
+/* set the presentation time stamp (pts) for the buffer to be sent next */
+static void set_next_pts(struct axd_dev *axd, unsigned int pipe, u64 pts)
+{
+ int ret;
+
+ if (!axd_get_flag(&axd->cmd.started_flg)) {
+ if (axd_ts_reset)
+ axd_ts_reset();
+ axd_set_flag(&axd->cmd.started_flg, 1);
+ }
+
+ if (axd_ts_adjust) {
+ ret = axd_ts_adjust(&pts);
+ if (ret)
+ dev_err(axd->dev, "Timestamp adjust failed\n");
+ }
+
+ axd->cmd.in_pipes[pipe].current_ts_high = pts >> 32;
+ axd->cmd.in_pipes[pipe].current_ts_low = pts & 0xffffffff;
+}
+
+/*
+ * note if we plan to support more than 1 AXD instance this will need to become
+ * an array indexed by device id.
+ */
+static struct axd_dev *__axd;
+
+/*
+ * only a single process can open an input/output device node at a time. And
+ * only that process can release that device node.
+ *
+ * semaphores ensure this behaviour.
+ */
+static int axd_open(struct inode *inode, struct file *filp)
+{
+ struct axd_dev *axd = container_of(inode->i_cdev, struct axd_dev, cdev);
+ unsigned int minor = MINOR(inode->i_rdev);
+ int type = minor_to_devtype(minor);
+ int ret;
+ int i;
+
+ /* save the inode for other methods */
+ filp->private_data = inode;
+
+ if (axd_get_flag(&axd->cmd.fw_stopped_flg))
+ return -EAGAIN;
+
+ switch (type) {
+ case AXD_CTRL:
+ /* nothing to do in here */
+ break;
+ case AXD_INPUT:
+ if ((filp->f_flags & O_ACCMODE) != O_WRONLY)
+ return -EPERM;
+
+ axd->cmd.nonblock = filp->f_flags & O_NONBLOCK;
+
+ ret = down_trylock(&axd->input_locks[MINOR_TO_INPUT(minor)]);
+ if (ret)
+ return -EBUSY;
+
+ /* Are any pipes running? */
+ for (i = 0; i < AXD_MAX_PIPES; i++) {
+ if (axd_cmd_inpipe_active(&axd->cmd, i))
+ goto pipes_running;
+ }
+
+ /* Invalidate any clock tracking from previous use */
+ axd_set_flag(&axd->cmd.started_flg, 0);
+pipes_running:
+
+ ret = axd_cmd_inpipe_start(&axd->cmd, MINOR_TO_INPUT(minor));
+ if (ret) {
+ up(&axd->input_locks[MINOR_TO_INPUT(minor)]);
+ return ret;
+ }
+
+ break;
+ case AXD_OUTPUT:
+ if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
+ return -EPERM;
+
+ axd->cmd.nonblock = filp->f_flags & O_NONBLOCK;
+
+ ret = down_trylock(&axd->output_locks[MINOR_TO_OUTPUT(minor)]);
+ if (ret)
+ return -EBUSY;
+ ret = axd_cmd_outpipe_start(&axd->cmd, MINOR_TO_OUTPUT(minor));
+ if (ret) {
+ up(&axd->output_locks[MINOR_TO_OUTPUT(minor)]);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(axd->dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int axd_release(struct inode *inode, struct file *filp)
+{
+ struct axd_dev *axd = container_of(inode->i_cdev, struct axd_dev, cdev);
+ unsigned int minor = MINOR(inode->i_rdev);
+ int type = minor_to_devtype(minor);
+
+ switch (type) {
+ case AXD_CTRL:
+ /* nothing to do in here */
+ break;
+ case AXD_INPUT:
+ axd_cmd_inpipe_stop(&axd->cmd, MINOR_TO_INPUT(minor));
+ up(&axd->input_locks[MINOR_TO_INPUT(minor)]);
+ break;
+ case AXD_OUTPUT:
+ axd_cmd_outpipe_stop(&axd->cmd, MINOR_TO_OUTPUT(minor));
+ up(&axd->output_locks[MINOR_TO_OUTPUT(minor)]);
+ break;
+ default:
+ dev_err(axd->dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t axd_read_log(struct axd_dev *axd,
+ char __user *buff, size_t count, loff_t *offp)
+{
+ void __iomem *log_addr;
+ unsigned int log_size;
+ static char *rbuf;
+ static int rbuf_rem;
+ int ret;
+
+ log_addr = axd->fw_base_m + axd_hdr_get_log_offset();
+ log_size = ioread32(log_addr+4);
+
+ if (!rbuf) {
+ /*
+ * first time we run, initialise
+ *
+ * TODO: should we free this? In normal operation this wouldn't
+ * be allocated, only if the user asked to print a log.
+ * Constantly allocating and freeing could cause fragmentation
+ * maybe..
+ */
+ dev_dbg(axd->ctrldev[0],
+ "allocating %u bytes for log buffer\n", log_size);
+ rbuf = kzalloc(log_size, GFP_KERNEL);
+ if (!rbuf)
+ return -ENOMEM;
+ }
+
+ if (!*offp) {
+ unsigned int flags = axd_platform_lock();
+ unsigned int log_offset = ioread32(log_addr);
+ unsigned int log_wrapped = ioread32(log_addr+8);
+ char __iomem *log_buff = (char __iomem *)(log_addr+12);
+
+ /* new read from beginning, fill up our internal buffer */
+ if (!log_wrapped) {
+ memcpy_fromio(rbuf, log_buff, log_offset);
+ rbuf_rem = log_offset;
+ } else {
+ char __iomem *pos = log_buff + log_offset;
+ unsigned int rem = log_size - log_offset;
+
+ memcpy_fromio(rbuf, pos, rem);
+ memcpy_fromio(rbuf + rem, log_buff, log_offset);
+ rbuf_rem = log_size;
+ }
+ axd_platform_unlock(flags);
+ }
+
+ if (count > rbuf_rem)
+ count = rbuf_rem;
+
+ ret = copy_to_user(buff, rbuf + *offp, count);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(axd->ctrldev[0], "read %d bytes from %d\n", count, (int)*offp);
+ *offp += count;
+ rbuf_rem -= count;
+
+ return count;
+}
+
+static ssize_t axd_read(struct file *filp, char __user *buff, size_t count,
+ loff_t *offp)
+{
+ struct inode *inode = filp->private_data;
+ struct axd_dev *axd = container_of(inode->i_cdev, struct axd_dev, cdev);
+ unsigned int minor = MINOR(inode->i_rdev);
+ unsigned int pipe = MINOR_TO_OUTPUT(minor);
+ ssize_t read = 0;
+
+ if (axd_get_flag(&axd->cmd.fw_stopped_flg))
+ return 0;
+
+ /* read the log when it's the ctrl device */
+ if (!minor)
+ return axd_read_log(axd, buff, count, offp);
+
+ if (axd_get_flag(&axd->timestamps_out_flg)) {
+ copy_to_user(buff, &axd->cmd.out_pipes[pipe].current_ts_low, 8);
+ read += 8;
+ buff += 8;
+ }
+
+ read += axd_cmd_recv_buffer(&axd->cmd, pipe, buff, count);
+ if (read > 0)
+ *offp += read;
+ return read;
+}
+
+static ssize_t axd_write(struct file *filp, const char __user *buff,
+ size_t count, loff_t *offp)
+{
+ struct inode *inode = filp->private_data;
+ struct axd_dev *axd = container_of(inode->i_cdev, struct axd_dev, cdev);
+ unsigned int minor = MINOR(inode->i_rdev);
+ unsigned int pipe = MINOR_TO_INPUT(minor);
+ ssize_t written;
+ struct axd_sync_data sync_data;
+
+ if (axd_get_flag(&axd->cmd.fw_stopped_flg))
+ return 0;
+
+ /* can't write ctrl device */
+ if (!minor)
+ return count;
+
+ if (count == sizeof(struct axd_sync_data)) {
+ /* Read sync data */
+ copy_from_user(&sync_data, buff, sizeof(sync_data));
+
+ /* Validate sync data */
+ if (sync_data.magic != SYNC_MGCNUM) {
+ /* Not valid sync data -- must be normal stream data */
+ goto stream_data;
+ }
+
+ set_next_pts(axd, pipe, sync_data.pts_us);
+ written = count;
+ } else {
+stream_data:
+ written = axd_cmd_send_buffer(&axd->cmd, pipe, buff, count);
+ }
+
+ if (written > 0)
+ *offp += written;
+ return written;
+}
+
+static const struct file_operations axd_fops = {
+ .owner = THIS_MODULE,
+ .open = axd_open,
+ .read = axd_read,
+ .write = axd_write,
+ .release = axd_release,
+};
+
+static int axd_create_chrdev(struct cdev *cdev,
+ const struct file_operations *fops, char *name)
+{
+ dev_t devno;
+ int ret;
+
+ ret = alloc_chrdev_region(&devno, 0, MAX_NUM_DEVICES, name);
+ if (ret < 0)
+ goto alloc_err;
+ cdev_init(cdev, fops);
+ ret = cdev_add(cdev, devno, MAX_NUM_DEVICES);
+ if (ret)
+ goto add_err;
+ return 0;
+add_err:
+ unregister_chrdev_region(devno, MAX_NUM_DEVICES);
+alloc_err:
+ return ret;
+}
+
+static void axd_destroy_chrdev(struct cdev *cdev)
+{
+ dev_t devno = cdev->dev;
+
+ cdev_del(cdev);
+ unregister_chrdev_region(devno, MAX_NUM_DEVICES);
+}
+
+#ifdef CONFIG_CRYPTO_LZO
+#include <linux/crypto.h>
+static int decompress_fw(struct axd_dev *axd, const struct firmware *fw)
+{
+ struct crypto_comp *tfm;
+ unsigned int size;
+ unsigned int fw_size = axd->fw_size;
+ char *cached_fw_base;
+ int ret = 0, i = 5;
+
+ tfm = crypto_alloc_comp("lzo", 0, 0);
+ if (IS_ERR(tfm)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ do {
+ /* allocate bigger memory for uncompressed fw */
+ dma_free_coherent(axd->dev, axd->fw_size,
+ axd->fw_base_m, axd->fw_base_p);
+ axd->fw_size = fw_size * i;
+ axd->fw_base_m = dma_alloc_coherent(axd->dev, axd->fw_size,
+ &axd->fw_base_p, GFP_KERNEL);
+ if (!axd->fw_base_m) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* first 4 bytes contain lzo magic number, skip them */
+ size = axd->fw_size;
+ cached_fw_base = (char *)((int)axd->fw_base_m & ~0x20000000);
+ ret = crypto_comp_decompress(tfm, fw->data+4,
+ fw->size-4, cached_fw_base, &size);
+
+ if (ret)
+ i++;
+ } while (ret && i < 10);
+
+ if (ret)
+ dev_err(axd->dev, "Failed to decompress the firmware\n");
+
+ crypto_free_comp(tfm);
+out:
+ return ret;
+}
+#else
+static int decompress_fw(struct axd_dev *axd, const struct firmware *fw)
+{
+ dev_err(axd->dev, "The firmware must be lzo decompressed first, compile driver again with CONFIG_CRYPTO_LZO enabled in kernel or do the decompression in user space.\n");
+ return -EIO;
+}
+#endif
+static int copy_fw(struct axd_dev *axd, const struct firmware *fw)
+{
+ int mgcnum = *(int *)fw->data;
+ int cached_fw_base = (int)axd->fw_base_m & ~0x20000000;
+
+ if (mgcnum != AXD_MGCNUM) {
+ if (mgcnum == LZO_MGCNUM)
+ return decompress_fw(axd, fw);
+
+ dev_err(axd->dev, "Not a valid firmware binary.\n");
+ return -EIO;
+ }
+ /*
+ * We copy through the cache, fw will do the necessary cache
+ * flushes and syncing at startup.
+ * Copying from uncached makes it more difficult for the
+ * firmware to keep the caches coherent with memory when it sets
+ * tlbs and start running.
+ */
+ memcpy_toio((void *)cached_fw_base, fw->data, fw->size);
+
+ /* TODO: do MD5 checksum verification */
+ return 0;
+}
+
+static void axd_free(struct axd_dev *axd)
+{
+ if (axd->buf_base_m)
+ dma_free_noncoherent(axd->dev, axd->inbuf_size+axd->outbuf_size,
+ axd->buf_base_m, axd->buf_base_p);
+ if (axd->fw_base_m)
+ dma_free_coherent(axd->dev, axd->fw_size,
+ axd->fw_base_m, axd->fw_base_p);
+}
+
+static int axd_alloc(struct axd_dev *axd)
+{
+ /* do the allocation once, return immediately if fw_base_m is set */
+ if (axd->fw_base_m)
+ return 0;
+
+ axd->fw_base_m = dma_alloc_coherent(axd->dev, axd->fw_size,
+ &axd->fw_base_p, GFP_KERNEL);
+ if (!axd->fw_base_m)
+ return -ENOMEM;
+
+ axd->buf_base_m = dma_alloc_noncoherent(axd->dev,
+ axd->inbuf_size+axd->outbuf_size,
+ &axd->buf_base_p, GFP_KERNEL);
+ if (!axd->buf_base_m) {
+ axd_free(axd);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int axd_fw_start(struct axd_dev *axd)
+{
+ unsigned long t0_new_pc;
+ unsigned int num_threads = axd_platform_num_threads();
+ struct axd_cmd *axd_cmd = &axd->cmd;
+ const struct firmware *fw;
+ int ret = 0, i;
+ char axd_name[16];
+ unsigned int gic_irq;
+
+ snprintf(axd_name, 16, "%s%d", AXD_NAME, axd->id);
+
+ /* request the firmware */
+ ret = request_firmware(&fw, "axd_firmware.bin", axd->ctrldev[0]);
+ if (ret) {
+ dev_err(axd->dev, "Failed to load firmware, check that firmware loading is setup correctly in userspace and kernel and that axd_firmware.bin is present in the FS\n");
+ goto out;
+ }
+
+ axd->fw_size = fw->size;
+ if (!axd->inbuf_size)
+ axd->inbuf_size = 0x7800;
+ if (!axd->outbuf_size)
+ axd->outbuf_size = 0x3c000;
+
+ ret = axd_alloc(axd);
+ if (ret) {
+ dev_err(axd->dev, "Failed to allocate memory for AXD f/w and buffers\n");
+ release_firmware(fw);
+ goto out;
+ }
+
+ dev_info(axd->dev, "Loading firmware at 0x%p ...\n", axd->fw_base_m);
+
+ ret = copy_fw(axd, fw);
+ release_firmware(fw);
+ if (ret)
+ goto out;
+
+ /* setup hdr and memmapped regs */
+ axd_hdr_init((unsigned long)axd->fw_base_m);
+ /* initialize the cmd structure and the buffers */
+ axd_cmd_init(axd_cmd,
+ axd_hdr_get_cmdblock_offset()+(unsigned long)axd->fw_base_m,
+ (unsigned long)axd->buf_base_m, axd->buf_base_p);
+
+ /*
+ * Tell AXD the frequency at which it's running and the IRQs
+ */
+ gic_irq = (axd->host_irq << 16) | axd->axd_irq;
+ iowrite32(gic_irq, &axd_cmd->message->gic_irq);
+ iowrite32(clk_get_rate(axd->clk)/1000000, &axd_cmd->message->freq);
+
+ axd_platform_init(axd);
+ for (i = 0; i < num_threads; i++) {
+ ret = axd_cmd_set_pc(axd_cmd, i, axd_hdr_get_pc(i));
+ if (ret == -1) {
+ dev_err(axd->dev, "Failed to set PC of T%d\n", i);
+ goto out;
+ }
+ }
+ /* setup and start master thread */
+ t0_new_pc = axd_hdr_get_pc(0);
+ if (t0_new_pc == -1UL) {
+ ret = -1;
+ goto out;
+ }
+ t0_new_pc = (unsigned long) axd->fw_base_m + (t0_new_pc - 0xD0000000);
+ axd_platform_set_pc(t0_new_pc);
+ ret = axd_platform_start();
+ if (ret)
+ goto out;
+
+ /* install the IRQ */
+ ret = axd_cmd_install_irq(&axd->cmd, axd->irqnum);
+ if (ret) {
+ dev_err(axd->dev, "Failed to install IRQ %d, error %d\n",
+ axd->irqnum, ret);
+ goto out;
+ }
+
+ for (i = 0; i < AXD_LDFW_RETRIES; i++) {
+ ret = axd_wait_ready(axd_cmd->message);
+ if (!ret) {
+ /*
+ * Let the firmware know the address of the buffer
+ * region
+ */
+ ret = axd_write_reg(axd_cmd,
+ AXD_REG_BUFFER_BASE, axd->buf_base_p);
+ if (ret) {
+ dev_err(axd->dev,
+ "Failed to setup buffers base address\n");
+ goto out;
+ }
+ return 0;
+
+ }
+ }
+out:
+ axd_free(axd);
+ return ret;
+}
+
+static void axd_fw_stop(struct axd_dev *axd)
+{
+ axd_cmd_free_irq(&axd->cmd, axd->irqnum);
+ axd_platform_stop();
+}
+
+/*
+ * Stops the firmware, reload it, and start it back again to recover from a
+ * fatal error.
+ */
+static void axd_reset(struct work_struct *work)
+{
+ unsigned int major, minor, patch;
+ int i;
+
+ struct axd_dev *axd = container_of(work, struct axd_dev, watchdogwork);
+
+
+ /* if we got a fatal error, don't reset if watchdog is disabled */
+ if (unlikely(!axd->cmd.watchdogenabled))
+ return;
+
+ /* stop the watchdog timer until we restart */
+ del_timer(&axd->watchdogtimer);
+
+ if (!axd_get_flag(&axd->cmd.fw_stopped_flg)) {
+ /* ping the firmware by requesting its version info */
+ axd_cmd_get_version(&axd->cmd, &major, &minor, &patch);
+ if (!major && !minor && !patch) {
+ dev_warn(axd->dev, "Firmware stopped responding...\n");
+ axd_set_flag(&axd->cmd.fw_stopped_flg, 1);
+ } else {
+ goto out;
+ }
+ }
+
+ axd_platform_print_regs();
+ dev_warn(axd->dev, "Reloading AXD firmware...\n");
+
+ axd_fw_stop(axd);
+
+ /* Signal to any active tasks first */
+ for (i = 0; i < axd->num_inputs; i++) {
+ if (down_trylock(&axd->input_locks[i])) {
+ /* trylock failed, pipe in use */
+ axd_cmd_send_buffer_abort(&axd->cmd, i);
+ } else {
+ /*
+ * Increment semaphore as succeeding down_trylock
+ * decremented it
+ */
+ up(&axd->input_locks[i]);
+ }
+ }
+ for (i = 0; i < axd->num_outputs; i++) {
+ if (down_trylock(&axd->output_locks[i])) {
+ /* trylock failed, pipe in use */
+ axd_cmd_recv_buffer_abort(&axd->cmd, i);
+ } else {
+ /*
+ * Increment semaphore as succeeding down_trylock
+ * decremented it
+ */
+ up(&axd->output_locks[i]);
+ }
+ }
+
+ /* wake up any task sleeping on command response */
+ wake_up(&axd->cmd.wait);
+ /* give chance to user land tasks to react to the crash */
+ ssleep(2);
+
+ axd_fw_start(axd);
+
+ for (i = 0; i < axd->num_inputs; i++) {
+ if (down_trylock(&axd->input_locks[i]))
+ axd_cmd_inpipe_reset(&axd->cmd, i);
+ else
+ up(&axd->input_locks[i]);
+ }
+ for (i = 0; i < axd->num_outputs; i++) {
+ if (down_trylock(&axd->output_locks[i]))
+ axd_cmd_outpipe_reset(&axd->cmd, i);
+ else
+ up(&axd->output_locks[i]);
+ }
+
+ axd_set_flag(&axd->cmd.fw_stopped_flg, 0);
+out:
+ axd->watchdogtimer.expires = jiffies + WATCHDOG_TIMEOUT;
+ add_timer(&axd->watchdogtimer);
+}
+
+/*
+ * Schedule to perform a reset.
+ * We don't perform the reset directly because the request comes from atomic
+ * context, and resetting must be done from process context.
+ */
+void axd_schedule_reset(struct axd_cmd *cmd)
+{
+ struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd);
+
+ axd_set_flag(&axd->cmd.fw_stopped_flg, 1);
+ schedule_work(&axd->watchdogwork);
+}
+
+/*
+ * Verifies that the firmware is still running by reading the version every few
+ * seconds.
+ */
+static void axd_watchdog_timer(unsigned long arg)
+{
+ struct axd_dev *axd = (struct axd_dev *)arg;
+
+ /* skip if watchdog is not enabled */
+ if (unlikely(!axd->cmd.watchdogenabled))
+ goto out;
+
+ schedule_work(&axd->watchdogwork);
+ return;
+out:
+ mod_timer(&axd->watchdogtimer, jiffies + WATCHDOG_TIMEOUT);
+}
+
+static void axd_start_watchdog(struct axd_dev *axd)
+{
+ INIT_WORK(&axd->watchdogwork, axd_reset);
+ init_timer(&axd->watchdogtimer);
+ axd->watchdogtimer.function = axd_watchdog_timer;
+ axd->watchdogtimer.data = (unsigned long)axd;
+ axd->watchdogtimer.expires = jiffies + HZ;
+ add_timer(&axd->watchdogtimer);
+}
+
+static void axd_stop_watchdog(struct axd_dev *axd)
+{
+ del_timer(&axd->watchdogtimer);
+}
+
+static int axd_create(struct axd_dev *axd, int id)
+{
+ struct cdev *cdev = &axd->cdev;
+ struct device *device;
+ int ret = 0, i = 0, j = 0;
+ char axd_name[16];
+ unsigned int major, minor, patch;
+
+ snprintf(axd_name, 16, "%s%d", AXD_NAME, id);
+ axd->id = id;
+
+ axd_set_flag(&axd->timestamps_out_flg, 0);
+
+ if (!axd->class) {
+ /* Create a new class for AXD */
+ axd->class = class_create(THIS_MODULE, AXD_NAME);
+ if (IS_ERR(axd->class)) {
+ ret = PTR_ERR(axd->class);
+ dev_err(axd->dev, "Failed to create class, error %d\n",
+ ret);
+ goto class_err;
+ }
+ }
+
+ /* Create a new char device with our own new Major Number */
+ ret = axd_create_chrdev(cdev, &axd_fops, axd_name);
+ if (ret) {
+ dev_err(axd->dev, "Failed to create char device\n");
+ goto chr_dev_err;
+ }
+
+ /*
+ * ctrl device mainly used to do mixer control.
+ *
+ * NOTE: We should create ctrl devices in a loop, but since it's
+ * unlikely we'll need more than 1, keep things simple until proved
+ * required.
+ */
+ device = device_create(axd->class, NULL, CTRL_TO_DEVNO(cdev, 0), NULL,
+ "%sctrl", axd_name);
+ if (IS_ERR(device)) {
+ ret = PTR_ERR(device);
+ dev_err(axd->dev,
+ "Failed to create ctrl device, error %d\n", ret);
+ goto ctrl_dev_err;
+ }
+ device->platform_data = &axd->cmd;
+ axd->ctrldev[0] = device;
+
+ /* Setup and start the threads */
+ ret = axd_fw_start(axd);
+ if (ret) {
+ dev_err(axd->dev, "Failed to start\n");
+ ret = -EIO;
+ goto fw_start_err;
+ }
+
+ /*
+ * Verify that the firmware is ready. In normal cases the firmware
+ * should start immediately, but to be more robust we do this
+ * verification and give the firmware a chance of 3 seconds to be ready
+ * otherwise we exit in failure.
+ */
+ for (i = 0; i < AXD_LDFW_RETRIES; i++) {
+ axd_cmd_get_version(&axd->cmd, &major, &minor, &patch);
+ if (major || minor || patch) {
+ /* firmware is ready */
+ break;
+ }
+ /* if we couldn't read the version after 3 tries, error */
+ if (i == AXD_LDFW_RETRIES-1) {
+ dev_err(axd->dev, "Failed to communicate with the firmware\n");
+ ret = -EIO;
+ goto fw_start_err;
+ }
+ /* wait for 10 ms for the firmware to start */
+ mdelay(10);
+ }
+ dev_info(axd->dev, "Running firmware version %u.%u.%u %s\n",
+ major, minor, patch, axd_hdr_get_build_str());
+
+ /* Start watchdog timer */
+ axd_start_watchdog(axd);
+
+ /* Get num of input/output pipes */
+ ret = axd_cmd_get_num_pipes(&axd->cmd,
+ &axd->num_inputs, &axd->num_outputs);
+ if (ret) {
+ dev_err(axd->dev, "Failed to get numer of supported pipes\n");
+ ret = -EIO;
+ goto num_pipes_err;
+ }
+ axd->cmd.num_inputs = axd->num_inputs;
+ axd->cmd.num_outputs = axd->num_outputs;
+
+ /* Invalidate DCPP selector caches */
+ for (i = 0; i < axd->cmd.num_outputs; i++) {
+ axd->cmd.dcpp_channel_ctrl_cache[i] = -1;
+ axd->cmd.dcpp_band_ctrl_cache[i] = -1;
+ }
+
+ /* Create input/output locks to control access to the devices */
+ axd->input_locks = kcalloc(axd->num_inputs,
+ sizeof(struct semaphore), GFP_KERNEL);
+ if (!axd->input_locks) {
+ ret = -ENOMEM;
+ dev_err(axd->dev, "Couldn't create input locks\n");
+ goto input_locks_err;
+ }
+ axd->output_locks = kcalloc(axd->num_outputs,
+ sizeof(struct semaphore), GFP_KERNEL);
+ if (!axd->output_locks) {
+ ret = -ENOMEM;
+ dev_err(axd->dev, "Couldn't create output locks\n");
+ goto output_locks_err;
+ }
+
+ /* Setup sysfs for ctrl dev after f/w has started */
+ ret = axd_ctrl_sysfs_add(device);
+ if (ret) {
+ dev_err(axd->ctrldev[0], "Failed to create sysfs entries\n");
+ goto ctrl_sysfs_err;
+ }
+
+ /* Create input/output device nodes */
+ for (i = 0; i < axd->num_inputs; i++) {
+ device = device_create(axd->class, NULL,
+ INPUT_TO_DEVNO(cdev, i), NULL,
+ "%sinput%d", axd_name, i);
+ if (IS_ERR(device)) {
+ ret = PTR_ERR(device);
+ dev_err(axd->dev, "Failed to create input%d, error %d\n",
+ i, ret);
+ goto input_dev_err;
+ }
+ device->platform_data = &axd->cmd;
+ ret = axd_input_sysfs_add(device);
+ if (ret) {
+ dev_err(device, "Failed to create sysfs entries\n");
+ goto input_sysfs_err;
+ }
+ axd->inputdev[i] = device;
+ sema_init(&axd->input_locks[i], 1);
+ }
+ for (j = 0; j < axd->num_outputs; j++) {
+ device = device_create(axd->class, NULL,
+ OUTPUT_TO_DEVNO(cdev, j), NULL,
+ "%soutput%d", axd_name, j);
+ if (IS_ERR(device)) {
+ ret = PTR_ERR(device);
+ dev_err(axd->dev, "Failed to create output%d, error %d\n",
+ j, ret);
+ goto output_dev_err;
+ }
+ device->platform_data = &axd->cmd;
+ ret = axd_output_sysfs_add(device);
+ if (ret) {
+ dev_err(device, "Failed to create sysfs entries\n");
+ goto output_sysfs_err;
+ }
+ axd->outputdev[j] = device;
+ sema_init(&axd->output_locks[j], 1);
+ }
+
+ dev_info(axd->dev, "Created\n");
+ return 0;
+
+output_sysfs_err:
+ if (j < axd->num_outputs)
+ device_destroy(axd->class, OUTPUT_TO_DEVNO(cdev, j));
+output_dev_err:
+ /* We got an error midst creating devices, clean up the ones that were
+ * successfully created only */
+ for (j--; j >= 0; j--) {
+ axd_output_sysfs_remove(axd->outputdev[j]);
+ device_destroy(axd->class, OUTPUT_TO_DEVNO(cdev, j));
+ }
+input_sysfs_err:
+ if (i < axd->num_inputs)
+ device_destroy(axd->class, INPUT_TO_DEVNO(cdev, i));
+input_dev_err:
+ for (i--; i >= 0; i--) {
+ axd_input_sysfs_remove(axd->inputdev[i]);
+ device_destroy(axd->class, INPUT_TO_DEVNO(cdev, i));
+ }
+ axd_ctrl_sysfs_remove(axd->ctrldev[0]);
+ctrl_sysfs_err:
+ kfree(axd->output_locks);
+output_locks_err:
+ kfree(axd->input_locks);
+input_locks_err:
+num_pipes_err:
+ axd_stop_watchdog(axd);
+fw_start_err:
+ axd_fw_stop(axd);
+ device_destroy(axd->class, CTRL_TO_DEVNO(cdev, 0));
+ctrl_dev_err:
+ axd_destroy_chrdev(cdev);
+chr_dev_err:
+ class_destroy(axd->class);
+class_err:
+ return ret;
+}
+
+static void axd_destroy(struct axd_dev *axd)
+{
+ struct cdev *cdev = &axd->cdev;
+ int count, i;
+
+ axd_stop_watchdog(axd);
+ axd_fw_stop(axd);
+ count = axd->num_outputs;
+ for (i = count-1; i >= 0; i--) {
+ axd_output_sysfs_remove(axd->outputdev[i]);
+ device_destroy(axd->class, OUTPUT_TO_DEVNO(cdev, i));
+ }
+ count = axd->num_inputs;
+ for (i = count-1; i >= 0; i--) {
+ axd_input_sysfs_remove(axd->inputdev[i]);
+ device_destroy(axd->class, INPUT_TO_DEVNO(cdev, i));
+ }
+ axd_ctrl_sysfs_remove(axd->ctrldev[0]);
+ device_destroy(axd->class, CTRL_TO_DEVNO(cdev, 0));
+ kfree(axd->input_locks);
+ kfree(axd->output_locks);
+ axd_destroy_chrdev(cdev);
+ class_destroy(axd->class);
+ dev_info(axd->dev, "Removed\n");
+}
+
+static int axd_probe(struct platform_device *pdev)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ struct axd_platform_config *axd_pconfig = pdev->dev.platform_data;
+ u32 val[2] = {0, 0};
+ int ret = -EINVAL;
+
+ __axd = kzalloc(sizeof(struct axd_dev), GFP_KERNEL);
+ if (!__axd)
+ return -ENOMEM;
+
+ __axd->irqnum = platform_get_irq(pdev, 0);
+ if (__axd->irqnum < 0) {
+ dev_err(&pdev->dev, "Couldn't get parameter: 'irq'\n");
+ goto error;
+ }
+
+ if (of_node) {
+ ret = of_property_read_u32_array(of_node, "gic-irq", val, 2);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Operating without GIC in SWT1 mode\n");
+ } else {
+ __axd->host_irq = val[0];
+ __axd->axd_irq = val[1];
+ }
+
+ __axd->clk = of_clk_get(of_node, 0);
+ if (IS_ERR_OR_NULL(__axd->clk)) {
+ dev_err(&pdev->dev, "Couldn't get parameter: 'clocks'\n");
+ goto error;
+ }
+
+ ret = of_property_read_u32(of_node, "vpe", val);
+ if (!ret) {
+ if (!val[0]) {
+ dev_err(&pdev->dev, "'vpe' parameter can't be 0\n");
+ goto error;
+ }
+ __axd->vpe = val[0];
+ }
+
+ of_property_read_u32(of_node, "inbuf-size", &__axd->inbuf_size);
+ of_property_read_u32(of_node, "outbuf-size", &__axd->outbuf_size);
+ } else {
+ if (!axd_pconfig) {
+ dev_warn(&pdev->dev,
+ "No valid platform config was provided\n");
+ goto error;
+ }
+ __axd->host_irq = axd_pconfig->host_irq;
+ __axd->axd_irq = axd_pconfig->axd_irq;
+ __axd->clk = axd_pconfig->clk;
+ __axd->inbuf_size = axd_pconfig->inbuf_size;
+ __axd->outbuf_size = axd_pconfig->outbuf_size;
+
+ if (IS_ERR_OR_NULL(__axd->clk)) {
+ dev_err(&pdev->dev, "Must provide a valid clock\n");
+ goto error;
+ }
+ }
+
+ __axd->dev = &pdev->dev;
+
+ ret = axd_create(__axd, 0);
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ kfree(__axd);
+ return ret;
+}
+
+static void axd_remove(struct axd_dev *axd)
+{
+ axd_destroy(axd);
+ axd_free(axd);
+ kfree(axd);
+}
+
+static const struct of_device_id axd_match[] = {
+ { .compatible = "img,axd" },
+ {}
+};
+
+static struct platform_driver axd_driver = {
+ .driver = {
+ .name = "axd",
+ .owner = THIS_MODULE,
+ .of_match_table = axd_match,
+ },
+ .probe = axd_probe,
+};
+
+static int axd_register(void)
+{
+ return platform_driver_probe(&axd_driver, axd_probe);
+}
+
+static void axd_unregister(void)
+{
+ axd_remove(__axd);
+ return platform_driver_unregister(&axd_driver);
+}
+
+module_init(axd_register);
+module_exit(axd_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("AXD Audio Processing IP Driver");
diff --git a/drivers/char/axd/axd_module.h b/drivers/char/axd/axd_module.h
new file mode 100644
index 000000000000..4b4d040db5fe
--- /dev/null
+++ b/drivers/char/axd/axd_module.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * AXD is a hardware IP that provides various audio decoding capabilities for
+ * user applications, offloading the core on which the application is running
+ * and saving its valuable MIPS.
+ */
+#ifndef AXD_MODULE_H_
+#define AXD_MODULE_H_
+#include <linux/cdev.h>
+#include <linux/clk.h>
+
+#include "axd_api.h"
+#include "axd_cmds.h"
+
+#define MAX_CTRL_DEVICES 1
+#define MAX_IN_DEVICES AXD_MAX_PIPES
+#define MAX_OUT_DEVICES AXD_MAX_PIPES
+#define MAX_NUM_DEVICES (MAX_CTRL_DEVICES + MAX_IN_DEVICES + MAX_OUT_DEVICES)
+
+#define CTRL_TO_DEVNO(cdev, i) ((cdev)->dev+(i))
+#define INPUT_TO_DEVNO(cdev, i) (CTRL_TO_DEVNO((cdev), MAX_CTRL_DEVICES) + (i))
+#define OUTPUT_TO_DEVNO(cdev, i) (INPUT_TO_DEVNO((cdev), MAX_IN_DEVICES) + (i))
+
+#define MINOR_TO_CTRL(minor) (minor)
+#define MINOR_TO_INPUT(minor) ((minor) - MAX_CTRL_DEVICES)
+#define MINOR_TO_OUTPUT(minor) ((minor) - (MAX_IN_DEVICES + MAX_CTRL_DEVICES))
+
+void axd_schedule_reset(struct axd_cmd *cmd);
+
+
+/**
+ * struct axd_dev - axd device structure
+ * @cdev: char device structure
+ * @class: class structure
+ * @dev: pointer to struct device from platform_device
+ * @ctrldev: array of pointers to created ctrl devices
+ * (usually 1 only)
+ * @inputdev: array of pointers to created input devices
+ * @outputdev: array of pointers to created output devices
+ * @id: id of this axd device
+ * @num_inputs: number of inputs AXD hardware reported it can handle
+ * @num_outputs: number of outputs AXD hardware reported it provides
+ * @axd_cmd: axd_cmd structure
+ * @input_locks: semaphores to regulate access to input nodes
+ * @output_locks: semaphores to regulate access to output nodes
+ * @fw_base_m: pointer to mapped fw base address
+ * @fw_base_p: physical address of fw base
+ * @fw_size: size of reserved fw region
+ * @buf_base_m: pointer to mapped buffers base address
+ * @buf_base_p: physical address of buffers base
+ * @inbuf_size: size of reserved input buffers region
+ * @outbuf_size: size of reserved output buffers region
+ * @host_irq: gic irq of the host
+ * @axd_irq: gic irq of axd
+ * @irqnum: linux linear irq number for request_irq()
+ * @freq: clock frequency of axd counter
+ * @watchdogtimer: software watchdogtimer to check if axd is alive
+ * @watchdogwork: the work to execute to check if firwmare is still alive
+ * and restart if it discovers the firmware stopped
+ * responding.
+ * @timestamps_out_flg: a flag that indicates whether we should pass output
+ * timestamps or not
+ */
+struct axd_dev {
+ struct cdev cdev;
+ struct class *class;
+ struct device *dev;
+ struct device *ctrldev[MAX_CTRL_DEVICES];
+ struct device *inputdev[MAX_IN_DEVICES];
+ struct device *outputdev[MAX_OUT_DEVICES];
+ int id;
+ int num_inputs;
+ int num_outputs;
+ struct axd_cmd cmd;
+ struct semaphore *input_locks;
+ struct semaphore *output_locks;
+ void __iomem *fw_base_m;
+ dma_addr_t fw_base_p;
+ unsigned int fw_size;
+ void __iomem *buf_base_m;
+ dma_addr_t buf_base_p;
+ unsigned int inbuf_size;
+ unsigned int outbuf_size;
+ int host_irq;
+ int axd_irq;
+ int irqnum;
+ struct clk *clk;
+ unsigned int vpe;
+ struct timer_list watchdogtimer;
+ struct work_struct watchdogwork;
+ int timestamps_out_flg;
+};
+#endif /* AXD_MODULE_H_ */
diff --git a/include/linux/axd.h b/include/linux/axd.h
new file mode 100644
index 000000000000..08d184d8e38c
--- /dev/null
+++ b/include/linux/axd.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+#ifndef __AXD_H__
+#define __AXD_H__
+#include <linux/clk.h>
+
+/**
+ * struct axd_platform_config - axd platform configuration structure
+ * @host_irq: gic irq of host
+ * @axd_irq: gic irq of axd
+ * @vpe: vpe number on which axd should start
+ * @clk: clk struct for axd
+ * @inbuf_size: size of shared input buffers area.
+ * leave 0 for the driver to use the default 0x7800.
+ * @outbuf_size: size of shared output buffers area.
+ * leave 0 for the driver to use the default 0x3c000.
+ */
+struct axd_platform_config {
+ unsigned int host_irq;
+ unsigned int axd_irq;
+ unsigned int vpe;
+ struct clk *clk;
+ unsigned int inbuf_size;
+ unsigned int outbuf_size;
+};
+#endif /* __AXD_H_ */
--
2.1.0
More information about the Alsa-devel
mailing list