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@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: Grant Likely grant.likely@linaro.org Cc: Rob Herring robh+dt@kernel.org Cc: devicetree@vger.kernel.org Cc: alsa-devel@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_ */