[alsa-devel] [PATCH 00/11] Add AXD Audio Processing IP driver
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback. This series adds the driver and DT binding document.
I tried to split it into 'logical' units as there are many files and some of them are big (necessarily I believe). If it is still difficult to review as is I'd be happy to take on any suggestion to make things easier.
I added it to drivers/char as this is the best location I could think of at the moment. It doesn't fit in alsa subsystem but I Cced them to confirm. Also I'm not sure if new drivers should go to staging first then get promoted or they can be accepted directly.
This is based on Linus' 3.18-rc2
Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: devicetree@vger.kernel.org Cc: alsa-devel@alsa-project.org
Qais Yousef (11): MAINTANERS: Add AXD as a supported driver dt: bindings: add AXD Audio Processing IP binding document drivers: char: add AXD Audio Processing IP driver drivers: char: axd: add fw binary header manipulation files drivers: char: axd: add buffers manipulation files drivers: char: axd: add basic files for sending/receiving axd cmds drivers: char: axd: add cmd interfce helper functions drivers: char: axd: add low level AXD platform setup files drivers: char: axd: add sysfs setup files drivers: char: axd: add ts interface file drivers: char: axd: add Kconfig and Makefile
Documentation/devicetree/bindings/axd.txt | 39 + MAINTAINERS | 6 + drivers/char/Kconfig | 2 + drivers/char/Makefile | 2 + drivers/char/axd/Kconfig | 19 + drivers/char/axd/Makefile | 20 + drivers/char/axd/axd_api.h | 641 ++++++ drivers/char/axd/axd_buffers.c | 245 +++ drivers/char/axd/axd_buffers.h | 70 + drivers/char/axd/axd_cmds.c | 98 + drivers/char/axd/axd_cmds.h | 531 +++++ drivers/char/axd/axd_cmds_codec_internal.c | 58 + drivers/char/axd/axd_cmds_codec_internal.h | 28 + drivers/char/axd/axd_cmds_config.c | 1194 ++++++++++ drivers/char/axd/axd_cmds_decoder_config.c | 1806 ++++++++++++++++ drivers/char/axd/axd_cmds_encoder_config.c | 519 +++++ drivers/char/axd/axd_cmds_info.c | 1409 ++++++++++++ drivers/char/axd/axd_cmds_internal.c | 3237 ++++++++++++++++++++++++++++ drivers/char/axd/axd_cmds_internal.h | 306 +++ drivers/char/axd/axd_cmds_pipes.c | 1368 ++++++++++++ drivers/char/axd/axd_hdr.c | 69 + drivers/char/axd/axd_hdr.h | 20 + drivers/char/axd/axd_module.c | 1064 +++++++++ drivers/char/axd/axd_module.h | 99 + drivers/char/axd/axd_platform.h | 31 + drivers/char/axd/axd_platform_mips.c | 270 +++ drivers/char/axd/axd_sysfs.h | 37 + drivers/char/axd/axd_sysfs_ctrl.c | 539 +++++ drivers/char/axd/axd_sysfs_input.c | 406 ++++ drivers/char/axd/axd_sysfs_output.c | 1373 ++++++++++++ drivers/char/axd/axd_ts_driver.h | 24 + include/linux/axd.h | 32 + 32 files changed, 15562 insertions(+) create mode 100644 Documentation/devicetree/bindings/axd.txt create mode 100644 drivers/char/axd/Kconfig create mode 100644 drivers/char/axd/Makefile create mode 100644 drivers/char/axd/axd_api.h create mode 100644 drivers/char/axd/axd_buffers.c create mode 100644 drivers/char/axd/axd_buffers.h create mode 100644 drivers/char/axd/axd_cmds.c create mode 100644 drivers/char/axd/axd_cmds.h create mode 100644 drivers/char/axd/axd_cmds_codec_internal.c create mode 100644 drivers/char/axd/axd_cmds_codec_internal.h create mode 100644 drivers/char/axd/axd_cmds_config.c create mode 100644 drivers/char/axd/axd_cmds_decoder_config.c create mode 100644 drivers/char/axd/axd_cmds_encoder_config.c create mode 100644 drivers/char/axd/axd_cmds_info.c create mode 100644 drivers/char/axd/axd_cmds_internal.c create mode 100644 drivers/char/axd/axd_cmds_internal.h create mode 100644 drivers/char/axd/axd_cmds_pipes.c create mode 100644 drivers/char/axd/axd_hdr.c create mode 100644 drivers/char/axd/axd_hdr.h create mode 100644 drivers/char/axd/axd_module.c create mode 100644 drivers/char/axd/axd_module.h create mode 100644 drivers/char/axd/axd_platform.h create mode 100644 drivers/char/axd/axd_platform_mips.c create mode 100644 drivers/char/axd/axd_sysfs.h create mode 100644 drivers/char/axd/axd_sysfs_ctrl.c create mode 100644 drivers/char/axd/axd_sysfs_input.c create mode 100644 drivers/char/axd/axd_sysfs_output.c create mode 100644 drivers/char/axd/axd_ts_driver.h create mode 100644 include/linux/axd.h
AXD is Audio Processing IP driver that will be added in the following commits
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index dab92a78d1d5..b42c7386190e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1826,6 +1826,12 @@ F: include/uapi/linux/ax25.h F: include/net/ax25.h F: net/ax25/
+AXD AUDIO PROCESSING IP DRIVER +M: Qais Yousef qais.yousef@imgtec.com +S: Supported +F: Documentation/devicetree/bindings/axd.txt +F: drivers/char/axd/ + AZ6007 DVB DRIVER M: Mauro Carvalho Chehab m.chehab@samsung.com L: linux-media@vger.kernel.org
On Tue, Oct 28, 2014 at 11:26:19AM +0000, Qais Yousef wrote:
AXD is Audio Processing IP driver that will be added in the following commits
This should be after the addition of files, so last patch pls
Add device tree support for AXD Audio Processing IP
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: Rob Herring robh+dt@kernel.org Cc: Pawel Moll pawel.moll@arm.com Cc: Mark Rutland mark.rutland@arm.com Cc: Ian Campbell ijc+devicetree@hellion.org.uk Cc: Kumar Gala galak@codeaurora.org Cc: devicetree@vger.kernel.org Cc: alsa-devel@alsa-project.org --- Documentation/devicetree/bindings/axd.txt | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/axd.txt
diff --git a/Documentation/devicetree/bindings/axd.txt b/Documentation/devicetree/bindings/axd.txt new file mode 100644 index 000000000000..694fc40613fb --- /dev/null +++ b/Documentation/devicetree/bindings/axd.txt @@ -0,0 +1,39 @@ +* AXD Audio Processing IP Binding * + +Required properties: +- compatible: "img,axd" +- clocks: phandle for the clock that drives AXD. +- interrupts: the GIC interrupt where AXD is connected or software interrupt + 1 (not recommended). + +Optional properties: +- gic-irq: it takes two non-zero values, the first one is the host hwirq and + the second one is axd's. Host's hwirq should match the value in + interrupts. + If not using GIC, then axd will revert to using software interrupt 1 + which is a fallback mechanism for systems without GIC. It is + recommended to use GIC whenever possible. Some systems will only work + with GIC so this property will not be optional then. +- vpe: VPE number on which axd should start. Must be provided if axd is + running as a single VPE along Linux on the same core. + It can't be VPE0. + The VPE must be offlined by Linux before axd is loaded. +- inbuf-size: size of shared input buffers area. by default it's 0x7800 bytes. +- outbuf-size: size of shared output buffers area. by default it's 0x3c000 bytes. + + +Example: + + axdclk: axdclk@400M { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <400000000>; + }; + + axd: axd@07800000 { + compatible = "img,axd"; + clocks = <&axdclk>; + interrupts = <36 IRQ_TYPE_EDGE_RISING> + gic-irq = <36 37>; + vpe = <1>; + };
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_ */
On Tue, Oct 28, 2014 at 11:26:21AM +0000, Qais Yousef wrote:
AXD is Audio Processing IP by Imagination Technologies that can perform decoding, encoding, equalisation, resampling, mixing, synchronisation and audio playback.
Isn't that a codec? Why is this a "char" driver and not one that fits into our existing audio subsystem?
this patch adds defs and initialisation files
Spell check :)
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.
Do you really mean "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;
Please always use standard Linux kernel types, "u32" in this case.
thanks,
greg k-h
On 10/28/2014 02:10 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 11:26:21AM +0000, Qais Yousef wrote:
AXD is Audio Processing IP by Imagination Technologies that can perform decoding, encoding, equalisation, resampling, mixing, synchronisation and audio playback.
Isn't that a codec? Why is this a "char" driver and not one that fits into our existing audio subsystem?
As far as I understand it doesn't fit into current alsa framework because of a number of unsupported features that we need. Specifically the range of compressed decoders we support and their configurations and encoding. I might have gotten it wrong though so I'll wait to see what alsa people say.
this patch adds defs and initialisation files
Spell check :)
Hmm UK spelling vs US spelling :) Unless you mean the defs then I'll expand it.
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.
Do you really mean "any later version"?
Nope. I probably copied that from the wrong source.
- 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;
Please always use standard Linux kernel types, "u32" in this case.
OK.
Thanks, Qais
thanks,
greg k-h
On Tue, Oct 28, 2014 at 02:36:39PM +0000, Qais Yousef wrote:
On 10/28/2014 02:10 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 11:26:21AM +0000, Qais Yousef wrote:
AXD is Audio Processing IP by Imagination Technologies that can perform decoding, encoding, equalisation, resampling, mixing, synchronisation and audio playback.
Isn't that a codec? Why is this a "char" driver and not one that fits into our existing audio subsystem?
As far as I understand it doesn't fit into current alsa framework because of a number of unsupported features that we need. Specifically the range of compressed decoders we support and their configurations and encoding. I might have gotten it wrong though so I'll wait to see what alsa people say.
Then add the new features you need and everyone benefits.
Please work with the alsa developers on this, I will not accept anything until they say there is no way this will work, and they bless a custom one-off interface like this.
thanks,
greg k-h
On Tue, Oct 28, 2014 at 11:26:21AM +0000, Qais Yousef wrote:
+/* 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)
All these should really be ASoC codec/DSP register map and let DAPM and ASoC infrastructure handle these much better than you have done here
+/* 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;
+}
how is this different from ALSA timestamp and new work being done at to add start_at() APIs??
+/*
- 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;
ALSA does all this and much more, sigh!
+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;
+}
ALSA does data copy too!
these files provide functions to get information from the fw binary header
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_hdr.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/char/axd/axd_hdr.h | 20 ++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 drivers/char/axd/axd_hdr.c create mode 100644 drivers/char/axd/axd_hdr.h
diff --git a/drivers/char/axd/axd_hdr.c b/drivers/char/axd/axd_hdr.c new file mode 100644 index 000000000000..86a3aeec83e9 --- /dev/null +++ b/drivers/char/axd/axd_hdr.c @@ -0,0 +1,69 @@ +/* + * 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. + * + * Helper functions to parse AXD Header in the firmware binary. + */ +#include <linux/kernel.h> + +#include "axd_api.h" +#include "axd_hdr.h" + + +#ifdef DEBUG_HEADER +#define debughdr printk +#else +#define debughdr(format, ...) +#endif + +static struct axd_hdr *hdr; + +static void dump_hdr(void) +{ +#ifdef DEBUG_HEADER + unsigned int offset = 0; + unsigned long address = (unsigned long)hdr; + + debughdr("header <0x%08lX>:\n", address); + while (offset <= sizeof(*hdr)) { + debughdr("0x%08X\t", *(unsigned int *)(address+offset)); + offset += 4; + if ((offset % (4*4)) == 0) + debughdr("\n"); + } + debughdr("\n"); +#endif +} + +void axd_hdr_init(unsigned long address) +{ + hdr = (struct axd_hdr *)address; + dump_hdr(); +} + +unsigned long axd_hdr_get_pc(unsigned int thread) +{ + if (thread >= THREAD_COUNT) + return -1; + return hdr->thread_pc[thread]; +} + +unsigned long axd_hdr_get_cmdblock_offset(void) +{ + debughdr("cmdblock_offset = 0x%08X\n", hdr->cmd_block_offset); + return hdr->cmd_block_offset; +} + +char *axd_hdr_get_build_str(void) +{ + return hdr->build_str; +} + +unsigned long axd_hdr_get_log_offset(void) +{ + return hdr->log_offset; +} diff --git a/drivers/char/axd/axd_hdr.h b/drivers/char/axd/axd_hdr.h new file mode 100644 index 000000000000..34a46a7f8e65 --- /dev/null +++ b/drivers/char/axd/axd_hdr.h @@ -0,0 +1,20 @@ +/* + * 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. + * + * Helper functions to parse AXD Header in the firmware binary + */ + #ifndef AXD_HDR_H_ + #define AXD_HDR_H_ + +void axd_hdr_init(unsigned long address); +unsigned long axd_hdr_get_pc(unsigned int thread); +unsigned long axd_hdr_get_cmdblock_offset(void); +char *axd_hdr_get_build_str(void); +unsigned long axd_hdr_get_log_offset(void); + +#endif /* AXD_HDR_H_ */
these files support initilising and managing access to the shared buffers area in memory that is used to exchange data between AXD and linux
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_buffers.c | 245 +++++++++++++++++++++++++++++++++++++++++ drivers/char/axd/axd_buffers.h | 70 ++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 drivers/char/axd/axd_buffers.c create mode 100644 drivers/char/axd/axd_buffers.h
diff --git a/drivers/char/axd/axd_buffers.c b/drivers/char/axd/axd_buffers.c new file mode 100644 index 000000000000..fefa7f85f4db --- /dev/null +++ b/drivers/char/axd/axd_buffers.c @@ -0,0 +1,245 @@ +/* + * 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 generic buffer management API. + */ +#include <linux/err.h> +#include <linux/slab.h> + +#include "axd_buffers.h" + +#ifdef DEBUG_BUFFERS +#define debugbuf printk +#else +#define debugbuf(format, ...) +#endif + +/** + * axd_buffer_init - sets up axd buffer as a pool of fixed sized buffers. + * @address: starting address of the buffer as set up in the system + * @total_size: total size of available buffer + * @element_size: size of each buffer element + * + * axd_buffer_t *buffer is a memory pool of size @element_size and starting at + * address @address and of @total_size size. + */ +static int bufferq_init(struct axd_bufferq *bufferq, const char *name, + char *address, unsigned int num_elements, + unsigned int element_size, unsigned int nonblock) +{ + int i; + char **queue; + unsigned int *size; + + strncpy(bufferq->name, name, 16); + bufferq->stride = element_size; + bufferq->max = num_elements; + bufferq->rd_idx = 0; + bufferq->wr_idx = 0; + bufferq->nonblock = nonblock; + queue = kcalloc(num_elements, sizeof(char *), GFP_KERNEL); + if (!queue) + return -ENOMEM; + bufferq->queue = queue; + size = kcalloc(num_elements, sizeof(unsigned int), GFP_KERNEL); + if (!size) { + kfree(queue); + bufferq->queue = NULL; + return -ENOMEM; + } + bufferq->size = size; + /* + * setup the queue with all available buffer addresses if the base + * address is passed. Set it up as emptry if base address is NULL. + */ + if (address) { + for (i = 0; i < num_elements; i++) { + queue[i] = address + (element_size * i); + size[i] = element_size; + } + sema_init(&bufferq->rd_sem, num_elements); + sema_init(&bufferq->wr_sem, 0); + } else { + for (i = 0; i < num_elements; i++) { + queue[i] = NULL; + size[i] = element_size; + } + sema_init(&bufferq->rd_sem, 0); + sema_init(&bufferq->wr_sem, num_elements); + } + spin_lock_init(&bufferq->q_rdlock); + spin_lock_init(&bufferq->q_wrlock); + debugbuf("Initialized %s of %d elements of size %d bytes\n", + name, num_elements, element_size); + debugbuf("Address of %s: 0x%08X\n", name, (unsigned int)bufferq); + return 0; +} + +int axd_bufferq_init(struct axd_bufferq *bufferq, const char *name, + char *address, unsigned int num_elements, + unsigned int element_size, unsigned int nonblock) +{ + return bufferq_init(bufferq, + name, address, num_elements, element_size, nonblock); +} + +int axd_bufferq_init_empty(struct axd_bufferq *bufferq, const char *name, + unsigned int num_elements, unsigned int element_size, + unsigned int nonblock) +{ + return bufferq_init(bufferq, + name, NULL, num_elements, element_size, nonblock); +} + +void axd_bufferq_clear(struct axd_bufferq *bufferq) +{ + kfree(bufferq->queue); + kfree(bufferq->size); + bufferq->queue = NULL; + bufferq->size = NULL; +} + +/** + * axd_buffer_take - returns a valid buffer pointer + * @buffer: the buffers pool to be accessed + * + * This function will go into interruptible sleep if the pool is empty. + */ +char *axd_bufferq_take(struct axd_bufferq *bufferq, int *buf_size) +{ + char *buf; + int ret; + + if (!bufferq->queue) + return NULL; + + debugbuf("--(%s)-- taking new buffer\n", bufferq->name); + if (bufferq->nonblock) { + ret = down_trylock(&bufferq->rd_sem); + if (ret) + return ERR_PTR(-EAGAIN); + + } else { + ret = down_interruptible(&bufferq->rd_sem); + if (ret) + return ERR_PTR(-ERESTARTSYS); + if (bufferq->abort_take) { + bufferq->abort_take = 0; + return ERR_PTR(-ERESTARTSYS); + } + } + /* + * must ensure we have one access at a time to the queue and rd_idx + * to be preemption and SMP safe + * Sempahores will ensure that we will only read after a complete write + * has finished, so we will never read and write from the same location. + */ + spin_lock(&bufferq->q_rdlock); + buf = bufferq->queue[bufferq->rd_idx]; + if (buf_size) + *buf_size = bufferq->size[bufferq->rd_idx]; + bufferq->rd_idx++; + if (bufferq->rd_idx >= bufferq->max) + bufferq->rd_idx = 0; + spin_unlock(&bufferq->q_rdlock); + up(&bufferq->wr_sem); + debugbuf("--(%s)-- took buffer <0x%08X>\n", bufferq->name, + (unsigned int)buf); + return buf; +} + +/** + * axd_buffer_put - returns a buffer to the pool. + * @buffer: the buffers pool to be accessed + * @buf: the buffer to be returned. + * + * This function will go into interruptible sleep if the pool is full. + */ +int axd_bufferq_put(struct axd_bufferq *bufferq, char *buf, int buf_size) +{ + int ret; + + if (!bufferq->queue) + return 0; + + if (buf_size < 0) + buf_size = bufferq->stride; + + debugbuf("++(%s)++ returning buffer\n", bufferq->name); + if (bufferq->nonblock) { + ret = down_trylock(&bufferq->wr_sem); + if (ret) + return -EAGAIN; + + } else { + ret = down_interruptible(&bufferq->wr_sem); + if (ret) + return -ERESTARTSYS; + if (bufferq->abort_put) { + bufferq->abort_put = 0; + return -ERESTARTSYS; + } + } + /* + * must ensure we have one access at a time to the queue and wr_idx + * to be preemption and SMP safe. + * Semaphores will ensure that we only write after a complete read has + * finished, so we will never write and read from the same location. + */ + spin_lock(&bufferq->q_wrlock); + bufferq->queue[bufferq->wr_idx] = buf; + bufferq->size[bufferq->wr_idx] = buf_size; + bufferq->wr_idx++; + if (bufferq->wr_idx >= bufferq->max) + bufferq->wr_idx = 0; + spin_unlock(&bufferq->q_wrlock); + up(&bufferq->rd_sem); + debugbuf("++(%s)++ returned buffer <0x%08X>\n", bufferq->name, + (unsigned int)buf); + return 0; +} + +int axd_bufferq_is_full(struct axd_bufferq *bufferq) +{ + int ret; + /* + * if we can't put a buffer, then we're full. + */ + ret = down_trylock(&bufferq->wr_sem); + if (!ret) + up(&bufferq->wr_sem); + return ret; +} + +int axd_bufferq_is_empty(struct axd_bufferq *bufferq) +{ + int ret; + /* + * if we can't take more buffers, then its empty. + */ + ret = down_trylock(&bufferq->rd_sem); + if (!ret) + up(&bufferq->rd_sem); + return ret; +} + +void axd_bufferq_abort_take(struct axd_bufferq *bufferq) +{ + if (axd_bufferq_is_empty(bufferq)) { + bufferq->abort_take = 1; + up(&bufferq->rd_sem); + } +} + +void axd_bufferq_abort_put(struct axd_bufferq *bufferq) +{ + if (axd_bufferq_is_full(bufferq)) { + bufferq->abort_put = 1; + up(&bufferq->wr_sem); + } +} diff --git a/drivers/char/axd/axd_buffers.h b/drivers/char/axd/axd_buffers.h new file mode 100644 index 000000000000..745a5e28b36f --- /dev/null +++ b/drivers/char/axd/axd_buffers.h @@ -0,0 +1,70 @@ +/* + * 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 generic buffer management API. + */ +#ifndef AXD_BUFFERS_H_ +#define AXD_BUFFERS_H_ + +#include <linux/semaphore.h> +#include <linux/spinlock.h> + +/** + * struct axd_bufferq - axd buffer management structure + * @name: name of the buffer queue + * @stride: the space between buffers in memory + * @max: total number of buffers this queue can handle + * @rd_idx: read index of the circular buffer + * @wr_idx: write index of the circular buffer + * @rd_sem: semaphore to block when full + * @wr_sem: semaphore to block when empty + * @q_rdlock: smp critical section protection for reads + * @q_wrlock: smp critical section protection for writes + * @queue: array of pointers to buffer addresses + * @size: array of buffer's actual amount of data it has inside or it can + * store. + * @nonblock: return an error instead of block when empty/full + * @abort_take: abort any pending blocked take operation + * @abort_put: abort any pending blocked put operation + * + * axd_bufferq takes a contiguous memory region and divides it into smaller + * buffers regions of equal size and represents it as a queue. To avoid + * excessive locking it's done as a circular buffer queue. + */ +struct axd_bufferq { + char name[16]; + unsigned int stride; + unsigned int max; + unsigned int rd_idx; + unsigned int wr_idx; + struct semaphore rd_sem; + struct semaphore wr_sem; + spinlock_t q_rdlock; + spinlock_t q_wrlock; + char **queue; + unsigned int *size; + unsigned int nonblock; + unsigned int abort_take; + unsigned int abort_put; +}; + +int axd_bufferq_init(struct axd_bufferq *bufferq, const char *name, + char *address, unsigned int num_elements, + unsigned int element_size, unsigned int nonblock); +int axd_bufferq_init_empty(struct axd_bufferq *bufferq, const char *name, + unsigned int num_elements, unsigned int element_size, + unsigned int nonblock); +void axd_bufferq_clear(struct axd_bufferq *bufferq); +char *axd_bufferq_take(struct axd_bufferq *bufferq, int *buf_size); +int axd_bufferq_put(struct axd_bufferq *bufferq, char *buf, int buf_size); +int axd_bufferq_is_full(struct axd_bufferq *bufferq); +int axd_bufferq_is_empty(struct axd_bufferq *bufferq); +void axd_bufferq_abort_take(struct axd_bufferq *bufferq); +void axd_bufferq_abort_put(struct axd_bufferq *bufferq); + +#endif /* AXD_BUFFERS_H_ */
these files do the important part of talking with AXD to send and receive data buffers
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_cmds.c | 98 +++ drivers/char/axd/axd_cmds.h | 531 ++++++++++++++ drivers/char/axd/axd_cmds_pipes.c | 1368 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1997 insertions(+) create mode 100644 drivers/char/axd/axd_cmds.c create mode 100644 drivers/char/axd/axd_cmds.h create mode 100644 drivers/char/axd/axd_cmds_pipes.c
diff --git a/drivers/char/axd/axd_cmds.c b/drivers/char/axd/axd_cmds.c new file mode 100644 index 000000000000..229a0cd85d52 --- /dev/null +++ b/drivers/char/axd/axd_cmds.c @@ -0,0 +1,98 @@ +/* + * 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 Commands API - generic setup functions. + */ +#include "axd_api.h" +#include "axd_cmds.h" +#include "axd_cmds_internal.h" +#include "axd_module.h" + +static unsigned long __io_address; +static unsigned long __phys_address; + +void axd_cmd_init(struct axd_cmd *cmd, unsigned long cmd_address, + unsigned long io_address, unsigned long phys_address) +{ + int i; + + cmd->message = (struct axd_memory_map __iomem *)cmd_address; + mutex_init(&cmd->cm_lock); + init_waitqueue_head(&cmd->wait); + axd_set_flag(&cmd->response_flg, 0); + axd_set_flag(&cmd->fw_stopped_flg, 0); + for (i = 0; i < AXD_MAX_PIPES; i++) { + axd_cmd_inpipe_init(cmd, i); + axd_cmd_outpipe_init(cmd, i); + } + __io_address = io_address; + __phys_address = phys_address; + cmd->watchdogenabled = 1; + /* + * By default, always discard any pending buffers if an output device is + * closed before EOS is reached. + * This behaviour can be changed through sysfs. If discard is disabled, + * then upon closing an output device before EOS is reached, it'll + * resume from where it stopped. + */ + axd_set_flag(&cmd->discard_flg, 1); + axd_set_flag(&cmd->ctrlbuf_active_flg, 0); +} + +int axd_cmd_set_pc(struct axd_cmd *cmd, unsigned int thread, unsigned long pc) +{ + if (thread >= THREAD_COUNT) + return -1; + iowrite32(pc, &cmd->message->pc[thread]); + return 0; +} + +unsigned long axd_cmd_get_datain_address(struct axd_cmd *cmd) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + + return (unsigned long) axd->buf_base_m; +} + +unsigned long axd_cmd_get_datain_size(struct axd_cmd *cmd) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + + return axd->inbuf_size; +} + +unsigned long axd_cmd_get_dataout_address(struct axd_cmd *cmd) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + + return ((unsigned long) axd->buf_base_m) + axd->inbuf_size; +} + +unsigned long axd_cmd_get_dataout_size(struct axd_cmd *cmd) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + + return axd->outbuf_size; +} + +/* + * The driver understands IO address, while f/w understands physical addresses. + * A couple of helper functions to aid in converting when exchanging buffers. + * + * NOTE: + * buf must NOT be NULL - we want this as fast as possible, so omit the check + * for NULLl + */ +inline char *axd_io_2_phys(const char *buf) +{ + return (char *)(buf - __io_address + __phys_address); +} +inline char *axd_phys_2_io(const char *buf) +{ + return (char *)(buf - __phys_address + __io_address); +} diff --git a/drivers/char/axd/axd_cmds.h b/drivers/char/axd/axd_cmds.h new file mode 100644 index 000000000000..150098a99d92 --- /dev/null +++ b/drivers/char/axd/axd_cmds.h @@ -0,0 +1,531 @@ +/* + * 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 API commands Helper functions. + */ +#ifndef AXD_CMDS_H_ +#define AXD_CMDS_H_ + +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/semaphore.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include "linux/workqueue.h" + +#include "axd_api.h" +#include "axd_buffers.h" + +/** + * struct axd_desc_ctrl - axd desctriptors control structure + * @rd_idx: read index of next available descriptor + * @wr_idx: write index of empty slot ot return a descriptor to + * @rd_sem: semaphore to block when no more descriptors are available + * @wr_sem: semaphore to block when all descriptors are available + * @rd_lock: smp critical section protection for reads + * @wr_lock: smp critical section protection for writes + * @buf_desc: pointer to iomem where the descriptors are + * @num_desc: total number of descriptors provided by axd + * + * axd has a number of input and output descriptors to pass buffers around, this + * structure provides a mean for the driver to manage access to these + * descriptors. + */ +struct axd_desc_ctrl { + unsigned int rd_idx; + unsigned int wr_idx; + struct semaphore rd_sem; + struct semaphore wr_sem; + spinlock_t rd_lock; + spinlock_t wr_lock; + struct axd_buffer_desc __iomem *buf_desc; + unsigned int num_desc; +}; + +struct axd_cmd; + +/** + * struct axd_pipe - axd pipe management structure + * @work: work for top half of the interrupt + * @desc_ctrl: axd_desc_ctrl structure to manage this pipe's + * descriptors + * @desc_bufferq: buffer queue send through the descriptors + * @user_bufferq: buffer queue of buffers to be read by the user. only + * makes sense for an output pipe where the user doesn't + * have to read the returned buffers synchronously when we + * get an interrupt + * @cur_buf: pointer to the current user_bufferq being read + * @cur_buf_offset: offset of the current user_bufferq to start reading from + * @cur_buf_size: remaining size of data in current user_bufferq + * @discard_flg: a flag to indicate we should discard the remaining data + * if the user closed output node before reading all data, + * default to true. + * @enabled_flg: a flag indicates that this pipe is actively handling a + * stream + * @eos_flg: a flag indicates that eos was reached and we should do + * clean up as soon as possible + * @eos_mutex: for input pipes we need to protect against possible + * simulataneous sending of eos + * @intcount: number of interrupts received since last service. + * indicates the number of buffers services by axd. + * used by top half workqueue to know how many interrupts + * it needs to service in one go + * @id: pipe number or id + * @tsk: the userland task that opened this pipe + * @buf_size: the size of the buffer this pipe is configured to use + * @current_ts_low: lower half of the 64-bit timestamp for current buffer + * @current_ts_high: top half of the 64-bit timestamp for current buffer + * @cmd: pointer to axd_cmd struct for quick access + * @buf_desc: pointer to axd_buffer_desc struct for quick access + * + * axd could provide a number of pipes each of which handles a separate stream. + * this structure manages descriptors, buffers and other control bits associated + * with each input/output pipe. + */ +struct axd_pipe { + struct work_struct work; + struct axd_desc_ctrl desc_ctrl; + struct axd_bufferq desc_bufferq; + struct axd_bufferq user_bufferq; + char *cur_buf; + unsigned int cur_buf_offset; + unsigned int cur_buf_size; + unsigned int discard_flg; + unsigned int enabled_flg; + unsigned int eos_flg; + struct mutex eos_mutex; + atomic_t intcount; + unsigned int id; + struct task_struct *tsk; + unsigned int buf_size; + u32 current_ts_low; + u32 current_ts_high; + struct axd_cmd *cmd; + struct axd_buffer_desc __iomem *buf_desc; +}; + +/** + * struct axd_cmd - axd command structure + * @message: iomapped axd massage region, see axd_memory_map struct + * @cm_lock: mutex to control access to the message region + * @wait: wait for ControlCommand response, or command completion + * @response_flg: condition variable to wait on for response + * @in_workq: array of workqueues for input pipes + * @out_workq: array of workqueues for output pipes + * @in_pipes: array of input axd_pipe structs + * @out_pipes: array of output axd_pipe structs + * @watchdogenabled: software watchdog switch + * @discard_flg: master flag to control whether to discard data when user + * closes output node + * @nonblock: operate in nonblocking mode + * @fw_stopped_flg: this flag indicates that software watchdog detected that + * the firmware is not responding + * @num_inputs: number of input pipes + * @num_outputs: number of output pipes + * @ctrlbuf_active_flg: this flag indicates ctrl buffer mechanism is in use + * @dcpp_channel_ctrl_cache: dcpp channel configuration cache + * @dcpp_band_ctrl_cache: dcpp band configuration cache + * + * manage the iomapped area to exchange messages/commands with axd + */ +struct axd_cmd { + struct axd_memory_map __iomem *message; + struct mutex cm_lock; + wait_queue_head_t wait; + unsigned int response_flg; + struct workqueue_struct *in_workq; + struct workqueue_struct *out_workq; + struct axd_pipe in_pipes[AXD_MAX_PIPES]; + struct axd_pipe out_pipes[AXD_MAX_PIPES]; + int watchdogenabled; + unsigned int discard_flg; + unsigned int nonblock; + unsigned int fw_stopped_flg; + int num_inputs; + int num_outputs; + unsigned int ctrlbuf_active_flg; + int dcpp_channel_ctrl_cache[AXD_MAX_PIPES]; + int dcpp_band_ctrl_cache[AXD_MAX_PIPES]; + unsigned int started_flg; +}; + +static inline void axd_set_flag(unsigned int *flag, unsigned int value) +{ + *flag = value; + smp_wmb(); /* guarantee smp ordering */ +} + +static inline unsigned int axd_get_flag(unsigned int *flag) +{ + smp_rmb(); /* guarantee smp ordering */ + return *flag; +} + +#define CMD_TIMEOUT (1*HZ) + +/* Generic setup API */ +void axd_cmd_init(struct axd_cmd *cmd, unsigned long cmd_address, + unsigned long io_address, unsigned long phys_address); +int axd_cmd_set_pc(struct axd_cmd *cmd, unsigned int thread, unsigned long pc); +unsigned long axd_cmd_get_datain_address(struct axd_cmd *cmd); +unsigned long axd_cmd_get_datain_size(struct axd_cmd *cmd); +unsigned long axd_cmd_get_dataout_address(struct axd_cmd *cmd); +unsigned long axd_cmd_get_dataout_size(struct axd_cmd *cmd); + +/* Info API */ +void axd_cmd_get_version(struct axd_cmd *cmd, int *major, + int *minor, int *patch); +int axd_cmd_get_num_pipes(struct axd_cmd *cmd, unsigned int *inpipes, + unsigned int *outpipes); +void axd_cmd_get_decoders(struct axd_cmd *cmd, char *codecs); +void axd_cmd_get_encoders(struct axd_cmd *cmd, char *codecs); +int axd_cmd_xbar_present(struct axd_cmd *cmd); +int axd_cmd_mixer_get_eqenabled(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_mixer_get_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_mixer_get_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_mixer_get_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_mixer_get_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_mixer_get_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_mixer_get_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_mixer_get_mux(struct axd_cmd *cmd, unsigned int pipe, + char *mux); +int axd_cmd_input_get_enabled(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_input_get_source(struct axd_cmd *cmd, unsigned int pipe, + char *source); +void axd_cmd_input_get_codec(struct axd_cmd *cmd, unsigned int pipe, + char *codec); +void axd_cmd_input_get_gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_input_get_mute(struct axd_cmd *cmd, unsigned int pipe, + int *muted); +void axd_cmd_input_get_upmix(struct axd_cmd *cmd, unsigned int pipe, + char *upmix); +void axd_cmd_input_get_decoder_config(struct axd_cmd *cmd, unsigned int pipe, + char *config); +int axd_cmd_output_get_enabled(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_output_get_codec(struct axd_cmd *cmd, unsigned int pipe, + char *codec); +void axd_cmd_output_get_sink(struct axd_cmd *cmd, unsigned int pipe, + char *sink); +void axd_cmd_output_get_downmix(struct axd_cmd *cmd, unsigned int pipe, + char *downmix); +int axd_cmd_output_get_eqenabled(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_output_get_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_output_get_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_output_get_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_output_get_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_output_get_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_output_get_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain); +void axd_cmd_output_get_encoder_config(struct axd_cmd *cmd, unsigned int pipe, + char *config); +void axd_cmd_output_get_geq_power(struct axd_cmd *cmd, unsigned int pipe, + char *buf, int channel); +/* DCPP */ +int axd_cmd_output_dcpp_select_channel(struct axd_cmd *cmd, unsigned int pipe, + bool subband, unsigned int channel); +int axd_cmd_output_dcpp_select_band(struct axd_cmd *cmd, unsigned int pipe, + unsigned int band); + +unsigned int axd_cmd_output_get_dcpp_enabled(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_mode(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_channels(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_eq_mode(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_eq_bands(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_max_delay_samples(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_channel_delay_samples(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_eq_output_volume( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_eq_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_shift( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_shift( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_a0( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_a1( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_a2( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_b0( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_b1( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel); +unsigned int axd_cmd_output_get_dcpp_channel_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_channel_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_channel_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_channel_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_channel_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_channel_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_channel_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_bands(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_enabled(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_input_channel_mask( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_delay_samples(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_eq_output_volume( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_eq_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_a0( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_a1( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_a2( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_b0( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_b1( + struct axd_cmd *cmd, unsigned int pipe); +/* Config API */ +void axd_cmd_mixer_set_eqenabled(struct axd_cmd *cmd, unsigned int pipe, + int enable); +void axd_cmd_mixer_set_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_mixer_set_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_mixer_set_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_mixer_set_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_mixer_set_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_mixer_set_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_mixer_set_mux(struct axd_cmd *cmd, unsigned int pipe, + int mux); +int axd_cmd_input_set_enabled(struct axd_cmd *cmd, unsigned int pipe, + int enable); +void axd_cmd_input_set_source(struct axd_cmd *cmd, unsigned int pipe, + int source); +void axd_cmd_input_set_codec(struct axd_cmd *cmd, unsigned int pipe, + int codec); +void axd_cmd_input_set_gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_input_set_mute(struct axd_cmd *cmd, unsigned int pipe, + int mute); +void axd_cmd_input_set_upmix(struct axd_cmd *cmd, unsigned int pipe, + int upmix); +void axd_cmd_input_set_decoder_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config); +int axd_cmd_output_set_enabled(struct axd_cmd *cmd, unsigned int pipe, + int enable); +void axd_cmd_output_set_codec(struct axd_cmd *cmd, unsigned int pipe, + int codec); +void axd_cmd_output_set_sink(struct axd_cmd *cmd, unsigned int pipe, + int sink); +void axd_cmd_output_set_downmix(struct axd_cmd *cmd, unsigned int pipe, + int downmix); +void axd_cmd_output_set_eqenabled(struct axd_cmd *cmd, unsigned int pipe, + int enable); +void axd_cmd_output_set_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_output_set_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_output_set_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_output_set_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_output_set_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +void axd_cmd_output_set_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int gain); +/* DCPP */ +int axd_cmd_output_set_dcpp_enabled(struct axd_cmd *cmd, unsigned int pipe, + int enable); +int axd_cmd_output_set_dcpp_mode(struct axd_cmd *cmd, unsigned int pipe, + unsigned int mode); +int axd_cmd_output_set_dcpp_eq_mode(struct axd_cmd *cmd, unsigned int pipe, + unsigned int mode); +int axd_cmd_output_set_dcpp_channel_delay_samples(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_output_volume(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_passthrough_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, + unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_bass_shelf_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_bass_shelf_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_bass_shelf_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_bass_shelf_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_bass_shelf_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_bass_shelf_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_treble_shelf_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_treble_shelf_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_treble_shelf_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_treble_shelf_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_treble_shelf_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_treble_shelf_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_channel_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_enabled(struct axd_cmd *cmd, + unsigned int pipe, int enable); +int axd_cmd_output_set_dcpp_subband_input_channel_mask(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_delay_samples(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_output_volume(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_passthrough_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data); +int axd_cmd_output_set_dcpp_subband_low_pass_filter_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_low_pass_filter_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_low_pass_filter_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_low_pass_filter_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +int axd_cmd_output_set_dcpp_subband_low_pass_filter_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data); +void axd_cmd_output_set_encoder_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config); +unsigned int axd_cmd_info_get_resampler_fin(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_cmd_info_get_resampler_fout(struct axd_cmd *cmd, + unsigned int pipe); +void axd_cmd_info_set_resampler_fout(struct axd_cmd *cmd, unsigned int pipe, + unsigned int fout); +unsigned int axd_cmd_input_get_buffer_occupancy(struct axd_cmd *cmd, + unsigned int pipe); +void axd_cmd_input_set_buffer_occupancy(struct axd_cmd *cmd, unsigned int pipe, + unsigned int bo); + +/* Channel setup and access API */ +int axd_cmd_inpipe_start(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_inpipe_stop(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_inpipe_reset(struct axd_cmd *cmd, unsigned int pipe); +int axd_cmd_inpipe_active(struct axd_cmd *cmd, unsigned int pipe); +int axd_cmd_outpipe_start(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_outpipe_stop(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_outpipe_reset(struct axd_cmd *cmd, unsigned int pipe); +int axd_cmd_send_buffer(struct axd_cmd *cmd, unsigned int pipe, + const char __user *buf, unsigned int size); +void axd_cmd_send_buffer_abort(struct axd_cmd *cmd, unsigned int pipe); +int axd_cmd_recv_buffer(struct axd_cmd *cmd, unsigned int pipe, + char __user *buf, unsigned int size); +void axd_cmd_recv_buffer_abort(struct axd_cmd *cmd, unsigned int pipe); +int axd_cmd_install_irq(struct axd_cmd *cmd, unsigned int irqnum); +void axd_cmd_free_irq(struct axd_cmd *cmd, unsigned int irqnum); +int axd_cmd_reset_pipe(struct axd_cmd *cmd, unsigned int pipe); + +/* generic helper function required in several places */ +char *axd_io_2_phys(const char *buf); +char *axd_phys_2_io(const char *buf); + +/* Register write buffer */ +int axd_flush_reg_buf(struct axd_cmd *cmd); + +#endif /* AXD_CMDS_H_ */ diff --git a/drivers/char/axd/axd_cmds_pipes.c b/drivers/char/axd/axd_cmds_pipes.c new file mode 100644 index 000000000000..2e3ca7443647 --- /dev/null +++ b/drivers/char/axd/axd_cmds_pipes.c @@ -0,0 +1,1368 @@ +/* + * 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 Commands API - Pipes/Buffers Accessing functions. + */ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/uaccess.h> +#include <linux/wait.h> + +#include "axd_api.h" +#include "axd_cmds.h" +#include "axd_cmds_internal.h" +#include "axd_hdr.h" +#include "axd_module.h" +#include "axd_platform.h" + +/* + * axd_pipe->eos_flg for output pipes is overloaded to mean two things: + * + * - EOS_REACHED: indicates that firmware has processed all input buffers + * including EOS but userland hasn't read them all yet. + * + * - EOF_REACHED: indicates that firmware sent EOS back to us AND userland read + * all the data till EOS. + */ +#define EOS_REACHED 1 +#define EOF_REACHED 2 + +/* + * axd_pipe->enabled_flg for output pipes is overloaded to mean two things: + * + * - PIPE_STARTED: indicates that pipe was opened but no buffers were passed. + * When stopping the pipes, we know that we don't need to discard anything if + * the discard_flg is set in cmd struct. Which allows us to terminate easily + * and quickly. + * + * - PIPE_RUNNING: indicates that pipe has processed some buffers, so we should + * discard if user terminates early (and discard_flg is set in cmd struct). + */ +#define PIPE_STARTED 1 +#define PIPE_RUNNING 2 + +#ifdef DEBUG_DIAG +static unsigned int inSentCount[AXD_MAX_PIPES]; +static unsigned int inRecvCount[AXD_MAX_PIPES]; +static unsigned int outSentCount[AXD_MAX_PIPES]; +static unsigned int outRecvCount[AXD_MAX_PIPES]; +static unsigned int primeupCount[AXD_MAX_PIPES]; +static unsigned int read_size[AXD_MAX_PIPES]; +static unsigned int write_size[AXD_MAX_PIPES]; +static unsigned int recv_size[AXD_MAX_PIPES]; +#define debugdiag printk +#else +#define debugdiag(format, ...) +#endif + +static void axd_cmd_inpipe_clear(struct axd_cmd *cmd, unsigned int pipe); +static void axd_cmd_outpipe_clear(struct axd_cmd *cmd, unsigned int pipe); +static void axd_cmd_send_eos(struct axd_pipe *axd_pipe); +static void axd_output_prime_up(struct axd_pipe *axd_pipe); +static void axd_cmd_output_eos_reached(struct axd_cmd *cmd, unsigned int pipe); + +/* + * Send/Clear data{in, out} kicks. + * + * NOTE: + * Must acquire axd_platform_lock() before accessing kick and interrupt status + * registers as the AXD firmware might be accessing them at the same time. + */ +static inline void axd_datain_kick(struct axd_pipe *axd_pipe) +{ + unsigned long flags; + struct axd_memory_map __iomem *message = axd_pipe->cmd->message; + unsigned int pipe = axd_pipe->id; + unsigned int temp; + +#ifdef DEBUG_DIAG + inSentCount[pipe]++; +#endif + debugbuf("----> Send datain kick\n"); + flags = axd_platform_lock(); + temp = ioread32(&message->kick) | + AXD_ANY_KICK_BIT | AXD_KICK_DATA_IN_BIT; + iowrite32(temp, &message->kick); + temp = ioread32(&message->in_kick_count[pipe]) + 1; + iowrite32(temp, &message->in_kick_count[pipe]); + axd_platform_unlock(flags); + axd_platform_kick(); +} + +static inline void axd_dataout_kick(struct axd_pipe *axd_pipe) +{ + unsigned long flags; + struct axd_memory_map __iomem *message = axd_pipe->cmd->message; + unsigned int pipe = axd_pipe->id; + unsigned int temp; + +#ifdef DEBUG_DIAG + outSentCount[pipe]++; +#endif + debugbuf("----> Send dataout kick\n"); + flags = axd_platform_lock(); + temp = ioread32(&message->kick) | + AXD_ANY_KICK_BIT | AXD_KICK_DATA_OUT_BIT; + iowrite32(temp, &message->kick); + temp = ioread32(&message->out_kick_count[pipe]) + 1; + iowrite32(temp, &message->out_kick_count[pipe]); + axd_platform_unlock(flags); + axd_platform_kick(); +} + +/* Assumes axd_platform_lock() is already acquired before calling this */ +static inline int axd_datain_status_clear(struct axd_pipe *axd_pipe) +{ + struct axd_memory_map __iomem *message = axd_pipe->cmd->message; + unsigned int pipe = axd_pipe->id; + unsigned int intcount = ioread32(&message->in_int_count[pipe]); + + debugbuf("Clearing in_int_count[%u] = %u\n", pipe, intcount); + if (intcount == 0) + return -1; + atomic_add(intcount, &axd_pipe->intcount); + iowrite32(0, &message->in_int_count[pipe]); + return 0; +} + +/* Assumes axd_platform_lock() is already acquired before calling this */ +static inline int axd_dataout_status_clear(struct axd_pipe *axd_pipe) +{ + struct axd_memory_map __iomem *message = axd_pipe->cmd->message; + unsigned int pipe = axd_pipe->id; + unsigned int intcount = ioread32(&message->out_int_count[pipe]); + + debugbuf("Clearing out_int_count[%u] = %u\n", pipe, intcount); + if (intcount == 0) + return -1; + atomic_add(intcount, &axd_pipe->intcount); + iowrite32(0, &message->out_int_count[pipe]); + return 0; +} + +/* IRQ Handler */ +static irqreturn_t axd_irq(int irq, void *data) +{ + struct axd_cmd *cmd = data; + unsigned int int_status; + unsigned long flags; + int i, ret; + + axd_platform_irq_ack(); + flags = axd_platform_lock(); + int_status = ioread32(&cmd->message->int_status); + iowrite32(0, &cmd->message->int_status); + + if (!int_status) + goto out; + + debugbuf("<---- Received int_status = 0x%08X\n", int_status); + if (int_status & AXD_INT_KICK_DONE) + debugbuf("<---- Received kick done interrupt\n"); + if (int_status & AXD_INT_DATAIN) { + debugbuf("<---- Received datain interrupt\n"); + for (i = 0; i < AXD_MAX_PIPES; i++) { + struct axd_pipe *axd_pipe = &cmd->in_pipes[i]; + + if (axd_get_flag(&axd_pipe->enabled_flg)) { + ret = axd_datain_status_clear(axd_pipe); + if (!ret) + queue_work(cmd->in_workq, &axd_pipe->work); + } + } + } + if (int_status & AXD_INT_DATAOUT) { + debugbuf("<---- Received dataout interrupt\n"); + for (i = 0; i < AXD_MAX_PIPES; i++) { + struct axd_pipe *axd_pipe = &cmd->out_pipes[i]; + + if (axd_get_flag(&axd_pipe->enabled_flg)) { + ret = axd_dataout_status_clear(axd_pipe); + if (!ret && !axd_get_flag(&axd_pipe->eos_flg)) + queue_work(cmd->out_workq, &axd_pipe->work); + } + } + } + if (int_status & AXD_INT_CTRL) { + debugbuf("<---- Received ctrl interrupt\n"); + axd_set_flag(&cmd->response_flg, 1); + wake_up(&cmd->wait); + } + if (int_status & AXD_INT_ERROR) { + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + int error = ioread32(&cmd->message->error); + + debugbuf("<---- Received error interrupt\n"); + switch (error) { + default: + case 0: + break; + case 1: + dev_err(axd->dev, "Fatal error received...\n"); + axd_schedule_reset(cmd); + break; + case 2: + dev_warn(axd->dev, "Failed to set last configuration command\n"); + break; + } + + iowrite32(0, &cmd->message->error); + } +out: + /* + * ensure all writes to uncached shared memory are visible to AXD + * before releasing axd_platform_lock() + */ + wmb(); + axd_platform_unlock(flags); + return IRQ_HANDLED; +} + +/* + * Initialize the drivers descriptors control structre. + * @desc_ctrl: the desc control structure to initialize. + * @buf_desc: pointer to the buffer descriptor to control. + * @num_desc: total number of descriptors inside @buf_desc. + */ +static void desc_init(struct axd_desc_ctrl *desc_ctrl, + struct axd_buffer_desc __iomem *buf_desc, unsigned int num_desc) +{ + /* Reset ctrl desc struct */ + desc_ctrl->rd_idx = 0; + desc_ctrl->wr_idx = 0; + sema_init(&desc_ctrl->rd_sem, num_desc); + sema_init(&desc_ctrl->wr_sem, 0); + spin_lock_init(&desc_ctrl->rd_lock); + spin_lock_init(&desc_ctrl->wr_lock); + desc_ctrl->buf_desc = buf_desc; + desc_ctrl->num_desc = num_desc; +} + +/* + * Prepare a descriptor to be sent to firmware. + * @desc_ctrl: the control structure of the descriptor. + * @buf: physical address of the buffer to enqueue. + * @size: size of the buffer. + * @last: non-zero of this is the last buffer ie: EOS. + */ +static int desc_enqueue(struct axd_desc_ctrl *desc_ctrl, char *buf, + unsigned int size, int last, struct axd_pipe *chan) +{ + struct axd_buffer_desc __iomem *buf_desc = desc_ctrl->buf_desc; + unsigned int num_desc = desc_ctrl->num_desc; + unsigned int status_size = size | AXD_DESCRIPTOR_READY_BIT; + int ret; + + debugbuf("Enqueuing a descriptor, pipe[%u]... ", chan->id); + /* only proceed if we're not full */ + ret = down_trylock(&desc_ctrl->rd_sem); + if (ret) { + debugbuf("FAILED - full\n"); + return -1; + } + debugbuf("SUCCEEDED\n"); + + if (last) + status_size |= AXD_DESCRIPTOR_EOS_BIT; + + /* + * if we could lock the semaphore, then we're guaranteed that the + * current rd_idx is valid and ready to be used. So no need to verify + * that the status of the descriptor at rd_idx is valid. + */ + spin_lock(&desc_ctrl->rd_lock); + iowrite32(status_size, &buf_desc[desc_ctrl->rd_idx].status_size); + iowrite32((unsigned int)axd_io_2_phys(buf), + &buf_desc[desc_ctrl->rd_idx].data_ptr); + iowrite32(chan->current_ts_high, &buf_desc[desc_ctrl->rd_idx].pts_high); + iowrite32(chan->current_ts_low, &buf_desc[desc_ctrl->rd_idx].pts_low); + desc_ctrl->rd_idx++; + if (desc_ctrl->rd_idx >= num_desc) + desc_ctrl->rd_idx = 0; + spin_unlock(&desc_ctrl->rd_lock); + up(&desc_ctrl->wr_sem); /* we can dequeue 1 more item */ + return 0; +} + +/* + * Takes a buffer out of the descriptor queue. + * @desc_ctrl: the control structure of the descriptor. + * @size: sets it tot he size of the buffer returned if not NULL. + * @last: sets it to non-zero of this is the last buffer ie: EOS (if last is not + * NULL) + * + * On success, a valid pointer is received. NULL otherwise. + */ +static char *desc_dequeue(struct axd_desc_ctrl *desc_ctrl, unsigned int *size, + int *last, struct axd_pipe *chan, int is_out) +{ + struct axd_buffer_desc __iomem *buf_desc = desc_ctrl->buf_desc; + unsigned int num_desc = desc_ctrl->num_desc; + unsigned int status_size; + char *buf; + int ret; + + debugbuf("Dequeuing a descriptor, pipe[%u]... ", chan->id); + /* only proceed if we're not empty */ + ret = down_trylock(&desc_ctrl->wr_sem); + if (ret) { + debugbuf("FAILED - empty\n"); + return NULL; + } + spin_lock(&desc_ctrl->wr_lock); + status_size = ioread32(&buf_desc[desc_ctrl->wr_idx].status_size); + /* + * if ready and in_use bit are set, then the rest of the buffers are + * still owned by the AXD fw, we can't dequeue them then. exit. + */ + if ((status_size & AXD_DESCRIPTOR_INUSE_BIT) && + !(status_size & AXD_DESCRIPTOR_READY_BIT)) { + + debugbuf("SUCCEEDED\n"); + /* clear the in_use bit */ + iowrite32(status_size & ~AXD_DESCRIPTOR_INUSE_BIT, + &buf_desc[desc_ctrl->wr_idx].status_size); + + /* + * Return the pointer to the buffer and its size to caller. + * The caller might need to read it or return it to the pool. + */ + buf = (char *)ioread32(&buf_desc[desc_ctrl->wr_idx].data_ptr); + if (size) + *size = status_size & AXD_DESCRIPTOR_SIZE_MASK; + if (last) + *last = status_size & AXD_DESCRIPTOR_EOS_BIT; + + if (is_out) { + /* update any timestamps if is use */ + chan->current_ts_high = + ioread32(&buf_desc[desc_ctrl->wr_idx].pts_high); + chan->current_ts_low = + ioread32(&buf_desc[desc_ctrl->wr_idx].pts_low); + } + + desc_ctrl->wr_idx++; + if (desc_ctrl->wr_idx >= num_desc) + desc_ctrl->wr_idx = 0; + + spin_unlock(&desc_ctrl->wr_lock); + up(&desc_ctrl->rd_sem); /* we can enqueue 1 more item */ + return axd_phys_2_io(buf); + } + debugbuf("FAILED - AXD holds the rest of the descriptors\n"); + /* + * failed due to busy buffer, return writer locks + * as we haven't dequeued + */ + spin_unlock(&desc_ctrl->wr_lock); + up(&desc_ctrl->wr_sem); + return NULL; +} + +/* + * This is the function executed by the workqueue to process return input + * pipes descriptors. + * Each pipe will have its own version of this function executed when datain + * interrupt is received. + */ +static void in_desc_workq(struct work_struct *work) +{ + struct axd_pipe *axd_pipe = container_of(work, struct axd_pipe, work); + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + struct axd_cmd *cmd = axd_pipe->cmd; + unsigned int pipe = axd_pipe->id; + char *ret_buf; + int ret, last; + + debugbuf("*** Processing datain[%u] buffer ***\n", pipe); + do { /* we should have at least 1 desc to process */ + ret_buf = desc_dequeue(desc_ctrl, NULL, &last, axd_pipe, 0); + if (!ret_buf) + /* + * This could happen if an interrupt occurs while this + * work is already running, causing us to run twice in a + * row unnecessarily. Not harmful, so just return. + */ + return; +#ifdef DEBUG_DIAG + inRecvCount[pipe]++; +#endif + ret = axd_bufferq_put(desc_bufferq, ret_buf, -1); + if (ret) + return; + if (last) { + debugbuf("Received input[%u] EOS\n", pipe); + debugdiag("inSentCount[%u]= %u, inRecvCount[%u]= %u, write_size[%u]= %u\n", + pipe, inSentCount[pipe], + pipe, inRecvCount[pipe], + pipe, write_size[pipe]); + axd_cmd_inpipe_clear(cmd, pipe); + } + } while (!atomic_dec_and_test(&axd_pipe->intcount)); + + /* Do we need to send EOS? */ + if (axd_get_flag(&axd_pipe->eos_flg)) + axd_cmd_send_eos(axd_pipe); +} + +/* + * This is the function executed by the workqueue to process return output + * pipes descriptors. + * Each pipe will have its own version of this function executed when dataout + * interrupt is received. + */ +static void out_desc_workq(struct work_struct *work) +{ + struct axd_pipe *axd_pipe = container_of(work, struct axd_pipe, work); + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_bufferq *user_bufferq = &axd_pipe->user_bufferq; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + char *ret_buf; + unsigned int buf_size; + int ret, last; + + debugbuf("*** Processing dataout[%u] buffer ***\n", axd_pipe->id); + do { /* we should have at least 1 desc to process */ + ret_buf = desc_dequeue(desc_ctrl, + &buf_size, &last, axd_pipe, 1); + if (!ret_buf || axd_get_flag(&axd_pipe->eos_flg)) { + /* + * This could happen if an interrupt occurs while this + * work is already running, causing us to run twice in a + * row unnecessarily. Not harmful, so just return. + * + * OR if we prime up the output bufferq a tad too much + * we could end up with extra buffers after eos is + * reached, in this case we shouldn't process these + * extra buffers and just return. + */ + return; + } +#ifdef DEBUG_DIAG + outRecvCount[axd_pipe->id]++; + recv_size[axd_pipe->id] += buf_size; +#endif + if (likely(!axd_get_flag(&axd_pipe->discard_flg))) { + if (last) { + debugbuf("Received output[%u] EOS\n", + axd_pipe->id); + + axd_set_flag(&axd_pipe->eos_flg, EOS_REACHED); + } + ret = axd_bufferq_put(user_bufferq, ret_buf, buf_size); + if (ret) + return; + } else { /* drop all buffers until EOS is reached */ + if (last) { + debugbuf("Received output[%u] EOS - discard\n", + axd_pipe->id); + axd_set_flag(&axd_pipe->eos_flg, EOS_REACHED); + axd_cmd_output_eos_reached(axd_pipe->cmd, + axd_pipe->id); + return; + } + ret = axd_bufferq_put(desc_bufferq, ret_buf, -1); + if (ret) + return; + axd_output_prime_up(axd_pipe); + } + } while (!atomic_dec_and_test(&axd_pipe->intcount)); +} + +/* Send a stream flush command to firmware */ +static int axd_flush_input_stream(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_memory_map __iomem *message = cmd->message; + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + axd_set_flag(&cmd->response_flg, 0); + iowrite32(AXD_CTRL_CMD_FLUSH, &message->control_command); + iowrite32(pipe, &message->control_data); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->inputdev[pipe], "failed to flush input stream\n"); + return -1; + } + return 0; +} + +static int axd_flush_output_stream(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_memory_map __iomem *message = cmd->message; + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + axd_set_flag(&cmd->response_flg, 0); + iowrite32(AXD_CTRL_CMD_FLUSH, &message->control_command); + iowrite32(pipe + AXD_MAX_PIPES, &message->control_data); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->outputdev[pipe], "failed to flush output stream\n"); + return -1; + } + return 0; +} + +/* Send a reset buffer descriptor commands to firmware - input */ +static int axd_reset_input_bd(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_memory_map __iomem *message = cmd->message; + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + axd_set_flag(&cmd->response_flg, 0); + iowrite32(AXD_CTRL_CMD_RESET_BD, &message->control_command); + iowrite32(pipe, &message->control_data); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->inputdev[pipe], "failed to reset input buffer descriptors\n"); + return -1; + } + return 0; +} + +/* Send a reset buffer descriptor commands to firmware - output */ +static int axd_reset_output_bd(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_memory_map __iomem *message = cmd->message; + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + axd_set_flag(&cmd->response_flg, 0); + iowrite32(AXD_CTRL_CMD_RESET_BD, &message->control_command); + iowrite32(pipe + AXD_MAX_PIPES, &message->control_data); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->outputdev[pipe], "failed to reset output buffer descriptors\n"); + return -1; + } + return 0; +} +/* Send a reset pipe command to the firmware */ +int axd_cmd_reset_pipe(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_memory_map __iomem *message = cmd->message; + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + axd_set_flag(&cmd->response_flg, 0); + iowrite32(AXD_CTRL_CMD_RESET_PIPE, &message->control_command); + iowrite32(pipe, &message->control_data); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->ctrldev[0], "failed to reset pipe%d", pipe); + return -1; + } + return 0; +} + +/* Sends a dummy buffer indicating EOS to a pipe */ +static void axd_cmd_send_eos(struct axd_pipe *axd_pipe) +{ + struct axd_dev *axd = container_of(axd_pipe->cmd, struct axd_dev, cmd); + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + int ret; + char *p; + + mutex_lock(&axd_pipe->eos_mutex); + /* + * If eos is cleared, then a previous call successfully sent it, nothing + * to do then, so exit. + */ + if (!axd_get_flag(&axd_pipe->eos_flg)) + goto out; + + /* Only proceed if we know a buffer is available, don't block */ + if (axd_bufferq_is_empty(desc_bufferq)) + goto out; + p = axd_bufferq_take(desc_bufferq, NULL); + if (unlikely(IS_ERR(p))) + goto out; + ret = desc_enqueue(desc_ctrl, p, 0, 1, axd_pipe); + if (unlikely(ret)) { + /* shouldn't happen, print a warning */ + dev_warn(axd->inputdev[axd_pipe->id], "Warning, failed to enqueue buffer\n"); + goto out; + } + /* enqueued successfully, inform the axd firmware */ + axd_datain_kick(axd_pipe); + debugbuf("Sent input[%u] EOS\n", axd_pipe->id); + /* + * clear if eos sent successfully + */ + axd_set_flag(&axd_pipe->eos_flg, 0); +out: + mutex_unlock(&axd_pipe->eos_mutex); +} + +/* + * Send as many buffers to the output pipe as possible. + * Keeping the firmware output buffer primed up prevents the firmware from + * getting deprived of buffers to fill. + */ +static void axd_output_prime_up(struct axd_pipe *axd_pipe) +{ + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + unsigned int stride; + char *p; + int ret; + + /* + * Try not to send too much. Make sure to stop as soon as we receive + * EOS. + */ + if (axd_get_flag(&axd_pipe->eos_flg)) + return; + + /* prime up the output buffer as much as we can */ + while (!axd_bufferq_is_empty(desc_bufferq)) { +#ifdef DEBUG_DIAG + primeupCount[axd_pipe->id]++; +#endif + p = axd_bufferq_take(desc_bufferq, &stride); + if (IS_ERR(p)) + break; + ret = desc_enqueue(desc_ctrl, p, stride, 0, axd_pipe); + if (ret) { + /* + * error, return the buffer to the pool + */ + axd_bufferq_put(desc_bufferq, p, -1); + break; + } + /* inform axd firmware */ + axd_dataout_kick(axd_pipe); + } +} + +/* Exported functions */ +/* Initialize the input pipe structure */ +void axd_cmd_inpipe_init(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + + axd_pipe->cmd = cmd; + axd_pipe->buf_desc = cmd->message->input[pipe].descriptors; + axd_pipe->id = pipe; + + axd_set_flag(&axd_pipe->enabled_flg, 0); + axd_set_flag(&axd_pipe->eos_flg, 0); + mutex_init(&axd_pipe->eos_mutex); + atomic_set(&axd_pipe->intcount, 0); + + /* default buffer size, could be changed through sysfs */ + axd_pipe->buf_size = 1024*2; +} + +/* Initialize the output pipe structure */ +void axd_cmd_outpipe_init(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + + axd_pipe->cmd = cmd; + axd_pipe->buf_desc = cmd->message->output[pipe].descriptors; + axd_pipe->id = pipe; + + axd_set_flag(&axd_pipe->discard_flg, 0); + axd_set_flag(&axd_pipe->enabled_flg, 0); + axd_set_flag(&axd_pipe->eos_flg, 0); + atomic_set(&axd_pipe->intcount, 0); + + /* default buffer size, could be changed through sysfs */ + axd_pipe->buf_size = 1024*16; +} + +/* Set up the IRQ handler and workqueues */ +int axd_cmd_install_irq(struct axd_cmd *cmd, unsigned int irqnum) +{ + int i; + + cmd->in_workq = create_workqueue("axd_din_q"); + if (!cmd->in_workq) + return -ENOMEM; + for (i = 0; i < AXD_MAX_PIPES; i++) + INIT_WORK(&cmd->in_pipes[i].work, in_desc_workq); + cmd->out_workq = create_workqueue("axd_dout_q"); + if (!cmd->out_workq) { + destroy_workqueue(cmd->in_workq); + return -ENOMEM; + } + for (i = 0; i < AXD_MAX_PIPES; i++) + INIT_WORK(&cmd->out_pipes[i].work, out_desc_workq); + iowrite32(AXD_INT_KICK_DONE, &cmd->message->int_mask); + return request_irq(irqnum, axd_irq, 0, "axd_irq", cmd); +} + +void axd_cmd_free_irq(struct axd_cmd *cmd, unsigned int irqnum) +{ + flush_workqueue(cmd->in_workq); + destroy_workqueue(cmd->in_workq); + flush_workqueue(cmd->out_workq); + destroy_workqueue(cmd->out_workq); + free_irq(irqnum, cmd); +} + +/* + * Calculate the starting address of input pipe's buffers based on the + * information provided in firmware's header + */ +static char *axd_inpipe_datain_address(struct axd_cmd *cmd, unsigned int pipe, + unsigned int *num_avail_buffers) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + unsigned long base_address = axd_cmd_get_datain_address(cmd); + unsigned long total_size = axd_cmd_get_datain_size(cmd); + unsigned long num_desc, offset; + + /* + * Based on the defined axd_pipe->buf_size and number of input pipes + * supported by the firmware, we calculate the number of descriptors we + * need to use using this formula: + * + * axd_pipe->buf_size * num_desc = total_size / num_inputs + */ + num_desc = total_size / (cmd->num_inputs * axd_pipe->buf_size); + if (num_desc > AXD_INPUT_DESCRIPTORS) { + num_desc = AXD_INPUT_DESCRIPTORS; + } else if (num_desc == 0) { + dev_err(axd->inputdev[pipe], + "Error: input buffer element size is too large\n"); + return NULL; + } + offset = (total_size / cmd->num_inputs) * pipe; + if (num_avail_buffers) + *num_avail_buffers = num_desc; + + return (char *)(base_address + offset); +} + +static int axd_cmd_inpipe_buffers_init(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + struct axd_buffer_desc __iomem *in_buf_desc = axd_pipe->buf_desc; + unsigned int num_avail_buffers; + char bufname[16]; + int ret; + + char *buf_address = axd_inpipe_datain_address(cmd, pipe, + &num_avail_buffers); + if (!buf_address) + return -EIO; + + /* initialize descriptors & control semaphores/locks */ + desc_init(desc_ctrl, in_buf_desc, AXD_INPUT_DESCRIPTORS); + + /* initialize buffers */ + sprintf(bufname, "in_bufferq[%u]", pipe); + ret = axd_bufferq_init(&axd_pipe->desc_bufferq, bufname, buf_address, + num_avail_buffers, axd_pipe->buf_size, cmd->nonblock); + return ret; +} + +/* prepare inpipe for processing data */ +static int axd_cmd_inpipe_prepare(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + int ret; + + ret = axd_cmd_inpipe_buffers_init(cmd, pipe); + if (ret) + return ret; + + atomic_set(&axd_pipe->intcount, 0); + axd_set_flag(&axd_pipe->enabled_flg, PIPE_STARTED); + if (axd_reset_input_bd(cmd, pipe)) + goto out; + if (axd_flush_input_stream(cmd, pipe)) + goto out; + if (axd_cmd_input_set_enabled(cmd, pipe, 1)) + goto out; + + /* Set PTS values for streams received without sync data */ + axd_pipe->current_ts_high = -1; + axd_pipe->current_ts_low = -1; + + return 0; +out: + axd_set_flag(&axd_pipe->enabled_flg, 0); + return -EIO; +} + +/* Start processing data on input pipe @pipe */ +int axd_cmd_inpipe_start(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + int ret; + + /* + * If enabled is locked, it means that the firmware is still busy + * processing buffers until EOS is reached. Tell to try again shortly. + */ + if (axd_get_flag(&axd_pipe->enabled_flg)) + return -EAGAIN; + + debugbuf("Starting input[%u]\n", pipe); + ret = axd_cmd_inpipe_prepare(cmd, pipe); + if (ret) + return ret; + axd_pipe->tsk = current; +#ifdef DEBUG_DIAG + inSentCount[pipe] = 0; + inRecvCount[pipe] = 0; + write_size[pipe] = 0; +#endif + return 0; +} + +/* Stop processing data on input pipe @pipe */ +void axd_cmd_inpipe_stop(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + + debugbuf("Stopping input[%u]\n", pipe); + /* + * If we haven't sent any data to the firmware, then clear ourselves + * immediately without having to send EOS which could never return. + */ + if (axd_get_flag(&axd_pipe->discard_flg)) { + /* + * Setting eos indicates that an eos buffer need to be sent. In + * some cases (ie: error occurs in the application), the buffer + * queue would be full and eos would fail to send. When an + * interrupt is received then and a buffer becomes free, we + * send eos buffer if the eos flag is set. + */ + axd_set_flag(&axd_pipe->eos_flg, EOS_REACHED); + axd_cmd_send_eos(axd_pipe); + } else { + axd_cmd_inpipe_clear(cmd, pipe); + } + axd_pipe->tsk = NULL; +} + +/* clears input pipe so that it can be prepared to start again */ +static void axd_cmd_inpipe_clear(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + + /* disable input and clear buffers */ + axd_cmd_input_set_enabled(cmd, pipe, 0); + axd_bufferq_clear(&axd_pipe->desc_bufferq); + /* + * NOTE: disabling the enabled flag must be done at the end to make sure + * that the input device can't be opened again before everything else is + * cleared up properly. There was a race where setting enabled to 0 + * before clearing bufferq caused a crash as the device could be opened + * after the flag is disabled but before the bufferq is cleared so the + * bufferq would be setup then cleared again causing wrong memory access + * later when reading. + */ + axd_set_flag(&axd_pipe->enabled_flg, 0); + axd_set_flag(&axd_pipe->discard_flg, 0); +} + +/* Reset input pipe to starting state - for error recovery */ +void axd_cmd_inpipe_reset(struct axd_cmd *cmd, unsigned int pipe) +{ + axd_cmd_inpipe_clear(cmd, pipe); +} + +/* Is the input pipe active? */ +int axd_cmd_inpipe_active(struct axd_cmd *cmd, unsigned int pipe) +{ + int state = axd_get_flag(&cmd->in_pipes[pipe].enabled_flg); + return state == PIPE_STARTED || state == PIPE_RUNNING; +} + +/* + * Calculate the starting address of output pipe's buffers based on the + * information provided in firmware's header + */ +static char *axd_outpipe_dataout_address(struct axd_cmd *cmd, unsigned int pipe, + unsigned int *num_avail_buffers) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + unsigned long base_address = axd_cmd_get_dataout_address(cmd); + unsigned long total_size = axd_cmd_get_dataout_size(cmd); + unsigned long num_desc, offset; + + /* + * Based on the defined axd_pipe->buf_size and number of output pipes + * supported by the firmware, we calculate the number of descriptors we + * need to use using this formula: + * + * axd_pipe->buf_size * num_desc = total_size / num_outputs + */ + num_desc = total_size / (cmd->num_outputs * axd_pipe->buf_size); + if (num_desc > AXD_OUTPUT_DESCRIPTORS) { + num_desc = AXD_OUTPUT_DESCRIPTORS; + } else if (num_desc == 0) { + dev_err(axd->outputdev[pipe], "Error: output buffer element size is too large\n"); + return NULL; + } + offset = (total_size / cmd->num_outputs) * pipe; + if (num_avail_buffers) + *num_avail_buffers = num_desc; + + return (char *)(base_address + offset); +} + +static int axd_cmd_outpipe_buffers_init(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + struct axd_buffer_desc __iomem *out_buf_desc = axd_pipe->buf_desc; + unsigned int num_avail_buffers; + char bufname[16]; + int ret; + + char *buf_address = axd_outpipe_dataout_address(cmd, pipe, + &num_avail_buffers); + if (!buf_address) + return -EIO; + + /* initialise descriptors & control semaphores/locks */ + desc_init(desc_ctrl, out_buf_desc, AXD_OUTPUT_DESCRIPTORS); + /* intialise buffers */ + sprintf(bufname, "out_bufferq[%u]", pipe); + ret = axd_bufferq_init(&axd_pipe->desc_bufferq, + bufname, buf_address, + num_avail_buffers, axd_pipe->buf_size, + cmd->nonblock); + if (ret) + return ret; + sprintf(bufname, "user_bufferq[%u]", pipe); + ret = axd_bufferq_init_empty(&axd_pipe->user_bufferq, + bufname, num_avail_buffers, + axd_pipe->buf_size, cmd->nonblock); + if (ret) { + axd_bufferq_clear(&axd_pipe->desc_bufferq); + return ret; + } + + return ret; +} + +/* prepare outpipe for processing data */ +static int axd_cmd_outpipe_prepare(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + int ret; + + ret = axd_cmd_outpipe_buffers_init(cmd, pipe); + if (ret) + return ret; + + atomic_set(&axd_pipe->intcount, 0); + axd_set_flag(&axd_pipe->enabled_flg, PIPE_STARTED); + axd_set_flag(&axd_pipe->eos_flg, 0); + if (axd_reset_output_bd(cmd, pipe)) + goto out; + if (axd_cmd_output_set_enabled(cmd, pipe, 1)) + goto out; + return 0; +out: + axd_set_flag(&axd_pipe->enabled_flg, 0); + axd_set_flag(&axd_pipe->eos_flg, EOF_REACHED); + return -EIO; +} + +/* Start processing data on output pipe @pipe */ +int axd_cmd_outpipe_start(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + int ret; + + debugbuf("Starting output[%u]\n", pipe); + /* + * Fully initialise only if enabled is unlocked. + * If enabled is locked, it means someone opened the device then + * closed it before reaching EOS. In this case, re-enable output to + * continue reading from where we stopped. + */ + if (!axd_get_flag(&axd_pipe->enabled_flg)) { + ret = axd_cmd_outpipe_prepare(cmd, pipe); + if (ret) + return ret; + } else if (axd_get_flag(&axd_pipe->discard_flg)) { + /* + * we're still discarding some data from a previous call to + * stop, tell the user to try again shortly + */ + return -EAGAIN; + } + axd_pipe->tsk = current; +#ifdef DEBUG_DIAG + outSentCount[pipe] = 0; + outRecvCount[pipe] = 0; + primeupCount[pipe] = 0; + read_size[pipe] = 0; + recv_size[pipe] = 0; +#endif + return 0; +} + +/* Stop processing data on output pipe @pipe */ +void axd_cmd_outpipe_stop(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_bufferq *user_bufferq = &axd_pipe->user_bufferq; + char *ret_buf; + + debugbuf("Stopping output[%u]\n", pipe); + axd_pipe->tsk = NULL; + if (axd_get_flag(&cmd->discard_flg) && + axd_get_flag(&axd_pipe->enabled_flg)) { + /* Is there anything to discard? */ + if (axd_get_flag(&axd_pipe->enabled_flg) == PIPE_STARTED) { + /* + * nothing to clear up too, just disable the input so + * we'd initialise ourselves properly again on next + * start. + */ + axd_set_flag(&axd_pipe->enabled_flg, 0); + return; + } + axd_set_flag(&axd_pipe->discard_flg, 1); + + if (axd_pipe->cur_buf) + axd_bufferq_put(desc_bufferq, axd_pipe->cur_buf, -1); + + while (!axd_bufferq_is_empty(user_bufferq)) { + ret_buf = axd_bufferq_take(user_bufferq, NULL); + axd_bufferq_put(desc_bufferq, ret_buf, -1); + } + + if (axd_get_flag(&axd_pipe->eos_flg) == EOS_REACHED) { + axd_cmd_output_eos_reached(cmd, pipe); + return; + } + + axd_output_prime_up(axd_pipe); + + } + +} + +static void axd_cmd_outpipe_clear(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + /* + * unlock enabled to fully intialise next time we're + * opened. + */ + axd_flush_output_stream(cmd, pipe); + axd_bufferq_clear(&axd_pipe->desc_bufferq); + axd_bufferq_clear(&axd_pipe->user_bufferq); + axd_cmd_output_set_enabled(cmd, pipe, 0); + axd_set_flag(&axd_pipe->enabled_flg, 0); + axd_set_flag(&axd_pipe->discard_flg, 0); + axd_pipe->cur_buf = NULL; + axd_pipe->cur_buf_size = 0; + axd_pipe->cur_buf_offset = 0; +} + +/* Reset output pipe to starting state - for error recovery */ +void axd_cmd_outpipe_reset(struct axd_cmd *cmd, unsigned int pipe) +{ + axd_cmd_outpipe_clear(cmd, pipe); +} + +/* + * Send a buffer to input @pipe + * + * Returns number of bytes sent, or negative error number. + */ +int axd_cmd_send_buffer(struct axd_cmd *cmd, unsigned int pipe, + const char __user *buf, unsigned int size) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_desc_ctrl *desc_ctrl = &axd_pipe->desc_ctrl; + unsigned int stride; + int ret = 0; + int written = 0; + int diff; + unsigned int cp_size; + char *p; + + /* + * Before if we had no data buffer sent to the firmware EOS flag was + * sent perfect through, but now we shouldn't send EOS flag if + * no data was sent to the firmware. We use the discard variable to + * flag if we need to send the EOS at stop or not. + * see axd_cmd_inpipe_stop() + * NOTE: discard_flg for input pipe is different than discard_flg for + * output pipe. + */ + if (unlikely(!axd_get_flag(&axd_pipe->discard_flg))) + axd_set_flag(&axd_pipe->discard_flg, 1); + + debugbuf("Writing %u bytes [%u]\n", size, pipe); + while (written < size) { + /* + * There's a one to one mapping between the desc buffers and the + * descriptors owned by the driver. If the descriptors are + * empty, we'll sleep in here and when we wake up/proceed we are + * guaranteed that we will enqueue a descriptor successfully + */ + p = axd_bufferq_take(desc_bufferq, &stride); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + goto out; + } + diff = size - written; + cp_size = diff < stride ? diff : stride; + ret = copy_from_user(p, buf, cp_size); + if (ret) { + ret = -EFAULT; + goto out; + } + ret = desc_enqueue(desc_ctrl, p, cp_size, 0, axd_pipe); + if (unlikely(ret)) { + /* shouldn't happen, print a warning */ + dev_warn(axd->inputdev[pipe], "Warning, failed to enqueue buffer\n"); + goto out; + } + /* enqueued successfully, inform the axd firmware */ + axd_datain_kick(axd_pipe); + written += cp_size; + buf += cp_size; + + /* + * A time-based stream frame with PTS might have to be split + * over multiple buffers. We should only provide the PTS for + * the first buffer. The rest should have the PTS invalidated. + */ + axd->cmd.in_pipes[pipe].current_ts_high = -1; + axd->cmd.in_pipes[pipe].current_ts_low = -1; + } +out: + if (written) { +#ifdef DEBUG_DIAG + write_size[pipe] += written; +#endif + return written; + } + return ret; +} + +void axd_cmd_send_buffer_abort(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + + axd_bufferq_abort_take(desc_bufferq); +} + +static void axd_cmd_output_eos_reached(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + + /* display diag info only if chan is enabled */ + if (axd_get_flag(&axd_pipe->enabled_flg)) { + debugbuf("Output[%u] EOS reached\n", pipe); + debugdiag("outSentCount[%u]= %u, outRecvCount[%u]= %u, read_size[%u]= %u\n", + pipe, outSentCount[pipe], pipe, outRecvCount[pipe], + pipe, read_size[pipe]); + debugdiag("primeupCount[%u]= %u, recv_size[%u]= %u\n", + pipe, primeupCount[pipe], pipe, recv_size[pipe]); + + /* All buffers are read, clear them. */ + axd_cmd_outpipe_clear(cmd, pipe); + } +} + +/* + * Receive a buffer from output @pipe + * + * The logic in here is that buffers we can copy from are in user_bufferq which + * is filled when we get an interrupt that the axd firmware filled them up. + * desc_bufferq holds the buffers are yet to be serviced by the firmware. + * + * Returns number of bytes received, or negative error number. + */ +int axd_cmd_recv_buffer(struct axd_cmd *cmd, unsigned int pipe, + char __user *buf, unsigned int size) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_bufferq *user_bufferq = &axd_pipe->user_bufferq; + int ret = 0; + int read = 0; + int diff; + unsigned int cp_size; + unsigned int cur_buf_size, cur_buf_offset; + char *cur_buf = axd_pipe->cur_buf; + + if (axd_get_flag(&axd_pipe->eos_flg) == EOF_REACHED) { + axd_cmd_output_eos_reached(cmd, pipe); + return 0; + } + + axd_output_prime_up(axd_pipe); + + debugbuf("Reading %u bytes [%u]\n", size, pipe); + while (read < size) { + cur_buf_size = axd_pipe->cur_buf_size; + cur_buf_offset = axd_pipe->cur_buf_offset; + if (cur_buf_size) { + /* + * Current buffer points to the current user buffer + * we're holding and reading from. We keep hold into it + * until it is completely read. The logic is done in + * this way because the likelihood of this buffer to be + * larger than the read count is quite high if not the + * normal case everytime a read is issued. + */ + diff = size - read; + cp_size = diff < cur_buf_size ? diff : cur_buf_size; + ret = copy_to_user(buf, cur_buf+cur_buf_offset, + cp_size); + if (ret) + goto out; + read += cp_size; + buf += cp_size; + axd_pipe->cur_buf_offset += cp_size; + axd_pipe->cur_buf_size -= cp_size; +#ifdef DEBUG_DIAG + read_size[pipe] += cp_size; +#endif + } else { + /* + * Current user buffer is completely read, return it to + * the desc_bufferq and take another user buffer. + * Note that we will sleep on either putting or taking + * from the buffer if we're full/empty. ISR should + * fill our user buffer once more are available. + */ + if (cur_buf) { + ret = axd_bufferq_put(desc_bufferq, cur_buf, -1); + if (ret) + goto out; + if (axd_bufferq_is_empty(user_bufferq) && + axd_get_flag(&axd_pipe->eos_flg)) { + /* send EOF on next read */ + axd_set_flag(&axd_pipe->eos_flg, + EOF_REACHED); + /* + * Normally, we only need to clear up + * if read is 0. But, if the application + * is keeping track of where the stream + * ends, it might try to close the + * output pipe before the EOF is read. + * In this case, then the driver would + * lock up. Instead, we always clear up + * here to avoid this. + */ + axd_cmd_output_eos_reached(cmd, pipe); + goto out; + } + axd_output_prime_up(axd_pipe); + } + cur_buf = axd_bufferq_take(user_bufferq, &cp_size); + if (IS_ERR(cur_buf)) { + axd_pipe->cur_buf = NULL; + axd_pipe->cur_buf_offset = 0; + axd_pipe->cur_buf_size = 0; + /* + * if EOS is set and we get an error from + * bufferq_take then it is because we received a + * zero byte buffer with a EOS flag set (From + * the firmware), in this instance we just + * return EOF instead of the error code + * (ERESTARTSYS) + */ + if (axd_get_flag(&axd_pipe->eos_flg)) { + axd_set_flag(&axd_pipe->eos_flg, + EOF_REACHED); + ret = 0; + axd_cmd_output_eos_reached(cmd, pipe); + } else { + ret = PTR_ERR(cur_buf); + } + goto out; + } + axd_pipe->cur_buf_offset = 0; + axd_pipe->cur_buf_size = cp_size; + axd_pipe->cur_buf = cur_buf; + } + } +out: + if (read) { + axd_set_flag(&axd_pipe->enabled_flg, PIPE_RUNNING); + return read; + } + return ret; +} + +void axd_cmd_recv_buffer_abort(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + struct axd_bufferq *desc_bufferq = &axd_pipe->desc_bufferq; + struct axd_bufferq *user_bufferq = &axd_pipe->user_bufferq; + + axd_bufferq_abort_put(desc_bufferq); + axd_bufferq_abort_take(user_bufferq); +}
AXD has a lot of registers. These files contain helper functions to access these registers in a readable way.
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_cmds_codec_internal.c | 58 + drivers/char/axd/axd_cmds_codec_internal.h | 28 + drivers/char/axd/axd_cmds_config.c | 1194 ++++++++++ drivers/char/axd/axd_cmds_decoder_config.c | 1806 ++++++++++++++++ drivers/char/axd/axd_cmds_encoder_config.c | 519 +++++ drivers/char/axd/axd_cmds_info.c | 1409 ++++++++++++ drivers/char/axd/axd_cmds_internal.c | 3237 ++++++++++++++++++++++++++++ drivers/char/axd/axd_cmds_internal.h | 306 +++ 8 files changed, 8557 insertions(+) create mode 100644 drivers/char/axd/axd_cmds_codec_internal.c create mode 100644 drivers/char/axd/axd_cmds_codec_internal.h create mode 100644 drivers/char/axd/axd_cmds_config.c create mode 100644 drivers/char/axd/axd_cmds_decoder_config.c create mode 100644 drivers/char/axd/axd_cmds_encoder_config.c create mode 100644 drivers/char/axd/axd_cmds_info.c create mode 100644 drivers/char/axd/axd_cmds_internal.c create mode 100644 drivers/char/axd/axd_cmds_internal.h
diff --git a/drivers/char/axd/axd_cmds_codec_internal.c b/drivers/char/axd/axd_cmds_codec_internal.c new file mode 100644 index 000000000000..ba3ed9453faa --- /dev/null +++ b/drivers/char/axd/axd_cmds_codec_internal.c @@ -0,0 +1,58 @@ +/* + * 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. + * + * Common functionality required by the codec config files. + */ +#include "axd_cmds.h" +#include "axd_cmds_codec_internal.h" + +void str_append(char *dest, const char *src) +{ + int len = strlen(dest); + + dest += len; + strcpy(dest, src); +} + +void print_timeout_msg(char *config) +{ + strcpy(config, "Read Timeout\n\0"); +} + +/* Common decoder/encoder option parse functions */ + +void parse_pcm_samplerate(unsigned int samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} +void parse_pcm_channels(unsigned int channels, char *config) +{ + sprintf(config, "channels = %u\n", channels); +} +void parse_pcm_bitspersample(unsigned int bitspersample, char *config) +{ + sprintf(config, "bitspersample = %u\n", bitspersample); +} +void parse_pcm_justification(unsigned int justification, char *config) +{ +#define PCM_JUSTIFICATION_LEFT_STR "Left" +#define PCM_JUSTIFICATION_RIGHT_STR "Right" + const char *str; + + switch (justification) { + case 0: + str = PCM_JUSTIFICATION_LEFT_STR; + break; + case 1: + str = PCM_JUSTIFICATION_RIGHT_STR; + break; + default: + return; + } + sprintf(config, "justification = %s\n", str); +} diff --git a/drivers/char/axd/axd_cmds_codec_internal.h b/drivers/char/axd/axd_cmds_codec_internal.h new file mode 100644 index 000000000000..4ade17c210de --- /dev/null +++ b/drivers/char/axd/axd_cmds_codec_internal.h @@ -0,0 +1,28 @@ +/* + * 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. + * + * Common functionality required by the codec config files. + */ +#ifndef AXD_CMDS_CODEC_INTERNAL_H_ +#define AXD_CMDS_CODEC_INTERNAL_H_ + +void str_append(char *dest, const char *src); + +void print_timeout_msg(char *config); + +/* Common option parse functions */ +#define PCM_SAMPLERATE_PARAM "samplerate" +#define PCM_CHANNELS_PARAM "channels" +#define PCM_BITSPERSAMPLE_PARAM "bitspersample" +#define PCM_JUSTIFICATION_PARAM "justification" +void parse_pcm_samplerate(unsigned int samplerate, char *config); +void parse_pcm_channels(unsigned int channels, char *config); +void parse_pcm_bitspersample(unsigned int bitspersample, char *config); +void parse_pcm_justification(unsigned int justification, char *config); + +#endif diff --git a/drivers/char/axd/axd_cmds_config.c b/drivers/char/axd/axd_cmds_config.c new file mode 100644 index 000000000000..2d9e470e225f --- /dev/null +++ b/drivers/char/axd/axd_cmds_config.c @@ -0,0 +1,1194 @@ +/* + * 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 Commands API - Configuration functions. + */ +#include "axd_cmds.h" +#include "axd_cmds_internal.h" + + +/* + * Enable/Disable Mixer EQ. + * @pipe: pipe number. + * @enable: + * Enable = !0 + * Disable = 0 + */ +void axd_cmd_mixer_set_eqenabled(struct axd_cmd *cmd, unsigned int pipe, + int enable) +{ + unsigned int reg = AXD_REG_EQ_CTRL_GAIN; + unsigned int control; + + if (axd_read_reg(cmd, reg, &control)) + return; + + if (enable) + control |= AXD_EQCTRL_ENABLE_BITS; + else + control &= ~AXD_EQCTRL_ENABLE_BITS; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the Master gain of the EQ of the Mixer + * @pipe: pipe number. + * @gain: 0-99 gain value + */ +void axd_cmd_mixer_set_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = AXD_REG_EQ_CTRL_GAIN; + unsigned int control; + + if (unlikely(gain > 99 || gain < 0)) + return; + + if (axd_read_reg(cmd, reg, &control)) + return; + + gain = (gain << AXD_EQCTRL_GAIN_SHIFT) & AXD_EQCTRL_GAIN_BITS; + control &= ~AXD_EQCTRL_GAIN_BITS; + control |= gain; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the gain of the EQ Band0 of the Mixer + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_mixer_set_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, AXD_REG_EQ_BAND0, gain); +} + +/* + * Set the gain of the EQ Band1 of the Mixer + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_mixer_set_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, AXD_REG_EQ_BAND1, gain); +} + +/* + * Set the gain of the EQ Band2 of Mixer + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_mixer_set_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, AXD_REG_EQ_BAND2, gain); +} + +/* + * Set the gain of the EQ Band3 of the Mixer + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_mixer_set_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, AXD_REG_EQ_BAND3, gain); +} + +/* + * Set the gain of the EQ Band4 of the Mixer + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_mixer_set_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, AXD_REG_EQ_BAND4, gain); +} + +/* + * Select Mixer's Mux output + * @pipe: pipe number. + * @mux: + * Mix = 0 + * Input 0 = 1 + * Input 1 = 2 + * Input 2 = 3 + * Input 3 = 4 + */ +void axd_cmd_mixer_set_mux(struct axd_cmd *cmd, unsigned int pipe, + int mux) +{ + unsigned int reg = axd_get_mixer_mux_reg(cmd, pipe); + + if (unlikely(mux > 4 || mux < 0)) + return; + axd_write_reg(cmd, reg, mux); +} + +/* + * Enable/Disable input. + * @pipe: pipe number. + * @enable: + * Enable = !0 + * Disable = 0 + */ +int axd_cmd_input_set_enabled(struct axd_cmd *cmd, unsigned int pipe, + int enable) +{ + unsigned int reg = axd_get_input_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return -1; + + if (axd_read_reg(cmd, reg, &control)) + return -1; + + if (enable) + control |= AXD_INCTRL_ENABLE_BITS; + else + control &= ~AXD_INCTRL_ENABLE_BITS; + if (axd_write_reg(cmd, reg, control)) + return -1; + return 0; +} + +/* + * Set the source of the input pipe. + * @pipe: pipe number. + * @source: + * Pipe = 0 + * Aux = 1 + */ +void axd_cmd_input_set_source(struct axd_cmd *cmd, unsigned int pipe, + int source) +{ + unsigned int reg = axd_get_input_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || source > 1 || source < 0)) + return; + if (axd_read_reg(cmd, reg, &control)) + return; + source = (source << AXD_INCTRL_SOURCE_SHIFT) & AXD_INCTRL_SOURCE_BITS; + control &= ~AXD_INCTRL_SOURCE_BITS; + control |= source; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the codec of the input pipe. + * @pipe: pipe number. + * @codec: + * PCM Pass Through = 0 + * MPEG (2/3) = 1 + * Dolby AC3 = 2 + * AAC = 3 + * Ogg Vorbis = 4 + * FLAC = 5 + * Cook = 6 + * WMA = 7 + * DDPlus = 8 + * DTS = 9 Unsupported + * DTS-HD = 10 Unsupported + * ALAC = 11 + * SBC = 13 + */ +void axd_cmd_input_set_codec(struct axd_cmd *cmd, unsigned int pipe, + int codec) +{ + unsigned int reg = axd_get_input_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || codec > 13 || codec < 0 || + codec == 9 || codec == 10)) + return; + if (axd_read_reg(cmd, reg, &control)) + return; + codec = (codec << AXD_INCTRL_CODEC_SHIFT) & AXD_INCTRL_CODEC_BITS; + control &= ~AXD_INCTRL_CODEC_BITS; + control |= codec; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the gain of the input pipe. + * @pipe: pipe number. + * @gain: Signed 32 bit 2'compliment gain value. + * Gain Cut or Boost in 0.25dB increment. ie: 4 = 1dB. + */ +void axd_cmd_input_set_gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_input_gain_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + axd_write_reg(cmd, reg, gain); +} + +/* + * Mute/Unmute the input pipe. + * @pipe: pipe number. + * @mute: 0 = OFF + * !0 = ON + */ +void axd_cmd_input_set_mute(struct axd_cmd *cmd, unsigned int pipe, + int mute) +{ + unsigned int reg = axd_get_input_mute_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + axd_write_reg(cmd, reg, mute); +} + +/* + * Set the upmix of the input pipe. + * @pipe: pipe number. + * @upmix: + * Pass through = 0 + * Simple 5.1 = 1 + * Dolby Pro Logic 2 = 2 + */ +void axd_cmd_input_set_upmix(struct axd_cmd *cmd, unsigned int pipe, + int upmix) +{ + unsigned int reg = axd_get_input_upmix_reg(cmd, pipe); + + if (unlikely(!reg || upmix > 2 || upmix < 0)) + return; + axd_write_reg(cmd, reg, upmix); +} + +/* Set the buffer occupancy value of @pipe. */ +void axd_cmd_input_set_buffer_occupancy(struct axd_cmd *cmd, unsigned int pipe, + unsigned int bo) +{ + unsigned int reg = axd_get_input_buffer_occupancy_reg(cmd, pipe); + + axd_write_reg(cmd, reg, bo); +} + +/* + * Enable/Disable output. + * @pipe: pipe number. + * @enable: + * Enable = !0 + * Disable = 0 + */ +int axd_cmd_output_set_enabled(struct axd_cmd *cmd, unsigned int pipe, + int enable) +{ + unsigned int reg = axd_get_output_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return -1; + if (axd_read_reg(cmd, reg, &control)) + return -1; + if (enable) + control |= AXD_OUTCTRL_ENABLE_BITS; + else + control &= ~AXD_OUTCTRL_ENABLE_BITS; + if (axd_write_reg(cmd, reg, control)) + return -1; + return 0; +} + +/* + * Set the codec of the output pipe. + * @pipe: pipe number. + * @codec: + * PCM Pass Through = 0 + * MPEG (2/3) = 1 Unsupported + * Dolby AC3 = 2 Unsupported + * AAC = 3 Unsupported + * Ogg Vorbis = 4 Unsupported + * FLAC = 5 + * Cook = 6 Unsupported + * WMA = 7 Unsupported + * DDPlus = 8 Unsupported + * DTS = 9 Unsupported + * DTS-HD = 10 Unsupported + * ALAC = 11 + * SBC = 13 Unsupported + */ +void axd_cmd_output_set_codec(struct axd_cmd *cmd, unsigned int pipe, + int codec) +{ + unsigned int reg = axd_get_output_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || !(codec == 0 || codec == 5 || codec == 11))) + return; + if (axd_read_reg(cmd, reg, &control)) + return; + codec = (codec << AXD_OUTCTRL_CODEC_SHIFT) & AXD_OUTCTRL_CODEC_BITS; + control &= ~AXD_OUTCTRL_CODEC_BITS; + control |= codec; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the sink of the output pipe. + * @pipe: pipe number. + * @source: + * Pipe = 0 + */ +void axd_cmd_output_set_sink(struct axd_cmd *cmd, unsigned int pipe, + int sink) +{ + unsigned int reg = axd_get_output_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || (sink < 0 && sink > 3))) + return; + if (axd_read_reg(cmd, reg, &control)) + return; + sink = (sink << AXD_OUTCTRL_SINK_SHIFT) & AXD_OUTCTRL_SINK_BITS; + control &= ~AXD_OUTCTRL_SINK_BITS; + control |= sink; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the downmix of the output pipe. + * @pipe: pipe number. + * @downmix: + * Pass through = 0 + * 5.1 = 1 + * 2.0 = 2 + */ +void axd_cmd_output_set_downmix(struct axd_cmd *cmd, unsigned int pipe, + int downmix) +{ + unsigned int reg = axd_get_output_downmix_reg(cmd, pipe); + + if (unlikely(!reg || downmix > 2 || downmix < 0)) + return; + axd_write_reg(cmd, reg, downmix); +} + +/* + * Enable/Disable output EQ. + * @pipe: pipe number. + * @enable: + * Enable = !0 + * Disable = 0 + */ +void axd_cmd_output_set_eqenabled(struct axd_cmd *cmd, unsigned int pipe, + int enable) +{ + unsigned int reg = axd_get_output_eqcontrol_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return; + if (axd_read_reg(cmd, reg, &control)) + return; + + if (enable) + control |= AXD_EQCTRL_ENABLE_BITS; + else + control &= ~AXD_EQCTRL_ENABLE_BITS; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the Master gain of the EQ of output pipe. + * @pipe: pipe number. + * @gain: 0-99 gain value + */ +void axd_cmd_output_set_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_output_eqcontrol_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || gain > 99 || gain < 0)) + return; + if (axd_read_reg(cmd, reg, &control)) + return; + + gain = (gain << AXD_EQCTRL_GAIN_SHIFT) & AXD_EQCTRL_GAIN_BITS; + control &= ~AXD_EQCTRL_GAIN_BITS; + control |= gain; + axd_write_reg(cmd, reg, control); +} + +/* + * Set the gain of the EQ Band0 of output pipe. + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_output_set_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_output_eqband0_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, reg, gain); +} + +/* + * Set the gain of the EQ Band1 of output pipe. + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_output_set_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_output_eqband1_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, reg, gain); +} + +/* + * Set the gain of the EQ Band2 of output pipe. + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_output_set_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_output_eqband2_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, reg, gain); +} + +/* + * Set the gain of the EQ Band3 of output pipe. + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_output_set_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_output_eqband3_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, reg, gain); +} + +/* + * Set the gain of the EQ Band4 of output pipe. + * @pipe: pipe number. + * @gain: Signed 8 bit 2'compliment gain value. + */ +void axd_cmd_output_set_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int gain) +{ + unsigned int reg = axd_get_output_eqband4_reg(cmd, pipe); + + if (unlikely(!reg)) + return; + gain = (gain << AXD_EQBANDX_GAIN_SHIFT) & AXD_EQBANDX_GAIN_BITS; + axd_write_reg(cmd, reg, gain); +} + +/* DCPP */ + +int axd_cmd_output_set_dcpp_enabled(struct axd_cmd *cmd, unsigned int pipe, + int enable) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_control_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + if (axd_read_reg(cmd, reg, &control)) + return -1; + + if (enable) + control |= AXD_DCPP_CTRL_ENABLE_BITS; + else + control &= ~AXD_DCPP_CTRL_ENABLE_BITS; + + return axd_write_reg_buf(cmd, reg, control); +} + +int axd_cmd_output_set_dcpp_mode(struct axd_cmd *cmd, unsigned int pipe, + unsigned int mode) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_control_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + if (axd_read_reg(cmd, reg, &control)) + return -1; + + /* Conditionally mask in mode bit */ + control ^= ((control ^ (mode << AXD_DCPP_CTRL_MODE_SHIFT)) + & AXD_DCPP_CTRL_MODE_BITS); + + return axd_write_reg_buf(cmd, reg, control); +} + +int axd_cmd_output_set_dcpp_eq_mode(struct axd_cmd *cmd, unsigned int pipe, + unsigned int mode) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_control_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + if (axd_read_reg(cmd, reg, &control)) + return -1; + + /* Conditionally mask in mode bit */ + control ^= ((control ^ (mode << AXD_DCPP_CTRL_EQ_MODE_SHIFT)) + & AXD_DCPP_CTRL_EQ_MODE_BITS); + + return axd_write_reg_buf(cmd, reg, control); +} + +int axd_cmd_output_set_dcpp_channel_delay_samples(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_delay_samples_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_output_volume(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_eq_output_volume_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_passthrough_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_eq_passthrough_gain_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, + unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_eq_inverse_passthrough_gain_reg(cmd, + pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_bass_shelf_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_shift_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_bass_shelf_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_a0_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_bass_shelf_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_bass_shelf_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_bass_shelf_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_bass_shelf_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_treble_shelf_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_shift_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_treble_shelf_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_a0_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_treble_shelf_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_treble_shelf_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_treble_shelf_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_treble_shelf_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_gain_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a0_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a1_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a2_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b0_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b1_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_channel_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, + unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_shift_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_enabled(struct axd_cmd *cmd, + unsigned int pipe, int enable) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_control_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + if (axd_read_reg(cmd, reg, &control)) + return -1; + + if (enable) + control |= AXD_DCPP_CTRL_SUBBAND_ENABLE_BITS; + else + control &= ~AXD_DCPP_CTRL_SUBBAND_ENABLE_BITS; + + return axd_write_reg_buf(cmd, reg, enable); +} + +int axd_cmd_output_set_dcpp_subband_delay_samples(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_delay_samples_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_input_channel_mask(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_control_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + if (axd_read_reg(cmd, reg, &control)) + return -1; + + control &= ~AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_BITS; + control |= data << AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_SHIFT; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_output_volume(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_eq_output_volume_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_passthrough_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_eq_passthrough_gain_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_eq_inverse_passthrough_gain_reg(cmd, + pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_low_pass_filter_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + reg = axd_get_output_dcpp_subband_low_pass_filter_a0_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_low_pass_filter_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + reg = axd_get_output_dcpp_subband_low_pass_filter_a1_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_low_pass_filter_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + reg = axd_get_output_dcpp_subband_low_pass_filter_a2_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_low_pass_filter_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + reg = axd_get_output_dcpp_subband_low_pass_filter_b0_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_low_pass_filter_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int data) +{ + unsigned int reg; + + reg = axd_get_output_dcpp_subband_low_pass_filter_b1_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_gain_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a0_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a1_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a2_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b0_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b1_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} + +int axd_cmd_output_set_dcpp_subband_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band, unsigned int data) +{ + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_shift_reg(cmd, pipe); + + if (unlikely(!reg)) + return -1; + + return axd_write_reg_buf(cmd, reg, data); +} diff --git a/drivers/char/axd/axd_cmds_decoder_config.c b/drivers/char/axd/axd_cmds_decoder_config.c new file mode 100644 index 000000000000..4609b11bfcef --- /dev/null +++ b/drivers/char/axd/axd_cmds_decoder_config.c @@ -0,0 +1,1806 @@ +/* + * 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 Commands API - Decoder Configuration functions + */ +#include "axd_cmds.h" +#include "axd_cmds_codec_internal.h" +#include "axd_cmds_internal.h" +#include "axd_sysfs.h" + +/** PCM PASSTHROUGH (input) Config **/ +static void get_pcm_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_pcm_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + + parse_pcm_samplerate(data, config); + + reg = axd_get_decoder_pcm_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_pcm_channels(data, str); + str_append(config, str); + + reg = axd_get_decoder_pcm_bitspersample_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_pcm_bitspersample(data, str); + str_append(config, str); + + reg = axd_get_decoder_pcm_justification_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_pcm_justification(data, str); + str_append(config, str); +} + +static void set_pcm_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ + unsigned int reg; + int data; + + if (CMP_PARAM(config, PCM_SAMPLERATE_PARAM)) { + data = PARAM_VALUE(config, PCM_SAMPLERATE_PARAM); + switch (data) { + case 16000: + case 32000: + case 44100: + case 48000: + case 64000: + case 96000: + break; + default: + return; + } + reg = axd_get_decoder_pcm_samplerate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, PCM_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, PCM_CHANNELS_PARAM); + if (unlikely(data > 8 || data < 0)) + return; + reg = axd_get_decoder_pcm_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, PCM_BITSPERSAMPLE_PARAM)) { + data = PARAM_VALUE(config, PCM_BITSPERSAMPLE_PARAM); + switch (data) { + case 8: + case 16: + case 24: + case 32: + break; + default: + return; + } + reg = axd_get_decoder_pcm_bitspersample_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, PCM_JUSTIFICATION_PARAM)) { + data = PARAM_VALUE(config, PCM_JUSTIFICATION_PARAM); + if (unlikely(data > 1 || data < 0)) + return; + reg = axd_get_decoder_pcm_justification_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +/** MPEG (2/3) Config **/ +static void parse_mpeg_numchannels(unsigned int numchannels, char *config) +{ + sprintf(config, "numchannels = %u\n", numchannels); +} +static void parse_mpeg_mlchannel(unsigned int mlchannel, char *config) +{ + sprintf(config, "mlchannel = %u\n", mlchannel); +} +static void get_mpeg_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_mpeg_numchannels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_mpeg_numchannels(data, config); + + reg = axd_get_decoder_mpeg_mlchannel_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_mpeg_mlchannel(data, str); + str_append(config, str); +} + +static void set_mpeg_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define MPEG_NUMCHANNELS_PARAM "numchannels" +#define MPEG_MLCHANNEL_PARAM "mlchannel" + unsigned int reg; + int data; + + if (CMP_PARAM(config, MPEG_NUMCHANNELS_PARAM)) { + data = PARAM_VALUE(config, MPEG_NUMCHANNELS_PARAM); + if (unlikely(data > 0xFF || data < 0)) + return; + reg = axd_get_decoder_mpeg_numchannels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, MPEG_MLCHANNEL_PARAM)) { + data = PARAM_VALUE(config, MPEG_MLCHANNEL_PARAM); + if (unlikely(data > 0xFF || data < 0)) + return; + reg = axd_get_decoder_mpeg_mlchannel_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +/** Dolby AC3 Config **/ +static void parse_ac3_channels(unsigned int channels, char *config) +{ + sprintf(config, "channels = %u\n", channels); +} + +static void +parse_ac3_channel_order(unsigned int channels, char *config, size_t sz) +{ + static const char * const channel[] = { + "Left (0)", + "Right (1)", + "Centre (2)", + "Left Surround (3)", + "Right Surround (4)", + "L.F.E. (5)" + }; + + snprintf(config, sz, "channel-order:\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n", + channel[0], + channel[channels & 0xF], + channel[1], + channel[(channels >> 4) & 0xF], + channel[2], + channel[(channels >> 8) & 0xF], + channel[3], + channel[(channels >> 12) & 0xF], + channel[4], + channel[(channels >> 16) & 0xF], + channel[5], + channel[(channels >> 20) & 0xF] + ); + +} + +static void parse_ac3_mode(unsigned int mode, char *config) +{ + static const char * const modestr[] = { + "0 = Dual Mono Mode", + "1 = C (1/0)", + "2 = L,R (2/0)", + "3 = L,C,R (3/0)", + "4 = L,R,S (2/1)", + "5 = L,C,R,S (3/1)", + "6 = L,R,SL,SR,(2/2)", + "7 = L,C,R,SL,SR,(3/2)" + }; + static const char * const compmodestr[] = { "Line", "RF"}; + + sprintf(config, "mode = %s\n" + "compmode = %s\n", + modestr[mode&0xf], + compmodestr[mode >> 31]); +} + +static void get_ac3_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ +#undef BUFF_SIZE +#define BUFF_SIZE 64 + + unsigned int reg; + unsigned int data; + char str[BUFF_SIZE]; + + reg = axd_get_decoder_ac3_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ac3_channels(data, config); + + reg = axd_get_decoder_ac3_channel_order_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ac3_channel_order(data, str, BUFF_SIZE); + str_append(config, str); + + reg = axd_get_decoder_ac3_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ac3_mode(data, str); + str_append(config, str); +} + +static void set_ac3_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define AC3_CHANNELS_PARAM "channels" +#define AC3_CHANNEL_ORDER_PARAM "channel-order" +#define AC3_MODE_PARAM "mode" +#define AC3_COMPMODE_PARAM "compmode" + unsigned int reg; + int data, temp[6]; + + if (CMP_PARAM(config, AC3_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, AC3_CHANNELS_PARAM); + if (unlikely(data > 6 || data < 0)) + return; + reg = axd_get_decoder_ac3_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AC3_CHANNEL_ORDER_PARAM)) { + sscanf(config, "channel-order=%d,%d,%d,%d,%d,%d", + &temp[0], &temp[1], &temp[2], + &temp[3], &temp[4], &temp[5]); + data = ((temp[0] & 0xF) << 0) | + ((temp[1] & 0xF) << 4) | + ((temp[2] & 0xF) << 8) | + ((temp[3] & 0xF) << 12) | + ((temp[4] & 0xF) << 16) | + ((temp[5] & 0xF) << 20); + reg = axd_get_decoder_ac3_channel_order_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AC3_MODE_PARAM)) { + temp[0] = PARAM_VALUE(config, AC3_MODE_PARAM); + if (unlikely(temp[0] > 7 || temp[0] < 0)) + return; + reg = axd_get_decoder_ac3_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) + return; + data &= ~0xF; + data |= temp[0] & 0xF; + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AC3_COMPMODE_PARAM)) { + temp[0] = PARAM_VALUE(config, AC3_COMPMODE_PARAM); + if (unlikely(temp[0] > 1 || temp[0] < 0)) + return; + reg = axd_get_decoder_ac3_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) + return; + data &= ~0x80000000; + data |= (temp[0] & 0x1) << 31; + axd_write_reg(cmd, reg, data); + } } + +/** AAC Config **/ +static void parse_aac_version(unsigned int version, char *config) +{ +#define AAC_VERSION_0_STR "AAC Low Complexity" +#define AAC_VERSION_1_STR "MPEG4 LC" +#define AAC_VERSION_2_STR "MPEG4 HE" +#define AAC_VERSION_3_STR "MPEG4 DABPLUS" + const char *str; + + switch (version) { + case 0: + str = AAC_VERSION_0_STR; + break; + case 1: + str = AAC_VERSION_1_STR; + break; + case 2: + str = AAC_VERSION_2_STR; + break; + case 3: + str = AAC_VERSION_3_STR; + break; + default: + return; + } + sprintf(config, "version = %s\n", str); +} +static void parse_aac_channels(unsigned int channels, char *config) +{ + sprintf(config, "channels = %u\n", channels); +} +static void parse_aac_profile(unsigned int profile, char *config) +{ +#define AAC_PROFILE_0_STR "Main Profile (MP)" +#define AAC_PROFILE_1_STR "Low Complexity (LC)" +#define AAC_PROFILE_2_STR "Scalable Sample Rate (SSR)" + const char *str; + + switch (profile) { + case 0: + str = AAC_PROFILE_0_STR; + break; + case 1: + str = AAC_PROFILE_1_STR; + break; + case 2: + str = AAC_PROFILE_2_STR; + break; + default: + str = "Unknown"; + } + sprintf(config, "profile = %s\n", str); +} +static void parse_aac_streamtype(unsigned int streamtype, char *config) +{ +#define AAC_STREAMTYPE_0_STR "Auto Detect" +#define AAC_STREAMTYPE_1_STR "ADTS" +#define AAC_STREAMTYPE_2_STR "ADIF" +#define AAC_STREAMTYPE_3_STR "RAW" + const char *str; + + switch (streamtype) { + case 0: + str = AAC_STREAMTYPE_0_STR; + break; + case 1: + str = AAC_STREAMTYPE_1_STR; + break; + case 2: + str = AAC_STREAMTYPE_2_STR; + break; + case 3: + str = AAC_STREAMTYPE_3_STR; + break; + default: + str = "Unknown"; + } + sprintf(config, "streamtype = %s\n", str); +} + +static void parse_aac_samplerate(unsigned int samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} + +static void get_aac_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_aac_version_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_aac_version(data, config); + + reg = axd_get_decoder_aac_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_aac_channels(data, str); + str_append(config, str); + + reg = axd_get_decoder_aac_profile_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_aac_profile(data, str); + str_append(config, str); + + reg = axd_get_decoder_aac_streamtype_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_aac_streamtype(data, str); + str_append(config, str); + + reg = axd_get_decoder_aac_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_aac_samplerate(data, str); + str_append(config, str); +} + +static void set_aac_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define AAC_VERSION_PARAM "version" +#define AAC_CHANNELS_PARAM "channels" +#define AAC_PROFILE_PARAM "profile" +#define AAC_STREAMTYPE_PARAM "streamtype" +#define AAC_SAMPLERATE_PARAM "samplerate" + unsigned int reg; + int data; + + if (CMP_PARAM(config, AAC_VERSION_PARAM)) { + data = PARAM_VALUE(config, AAC_VERSION_PARAM); + if (unlikely(data > 3 || data < 0)) + return; + reg = axd_get_decoder_aac_version_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AAC_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, AAC_CHANNELS_PARAM); + reg = axd_get_decoder_aac_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AAC_PROFILE_PARAM)) { + data = PARAM_VALUE(config, AAC_PROFILE_PARAM); + if (unlikely(data > 2 || data < 0)) + return; + reg = axd_get_decoder_aac_profile_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AAC_STREAMTYPE_PARAM)) { + data = PARAM_VALUE(config, AAC_STREAMTYPE_PARAM); + if (unlikely(data > 3 || data < 0)) + return; + reg = axd_get_decoder_aac_streamtype_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, AAC_SAMPLERATE_PARAM)) { + data = PARAM_VALUE(config, AAC_SAMPLERATE_PARAM); + if (unlikely(data <= 0)) + return; + reg = axd_get_decoder_aac_samplerate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +/** Ogg Vorbis Config **/ +static void get_ogg_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + strcpy(config, "Ogg Vorbis Config not supported\n"); +} + +static void set_ogg_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +} + +/** FLAC Config **/ +static void parse_flac_channels(unsigned int channels, char *config) +{ + sprintf(config, "channels = %u\n", channels); +} +static void parse_flac_samplerate(unsigned int samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} +static void parse_flac_bitspersample(unsigned int bitspersample, char *config) +{ + sprintf(config, "bitspersample = %u\n", bitspersample); +} +static void parse_flac_md5checking(unsigned int md5checking, char *config) +{ + sprintf(config, "md5checking = %u\n", md5checking); +} +static void get_flac_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_flac_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_channels(data, config); + + reg = axd_get_decoder_flac_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_samplerate(data, str); + str_append(config, str); + + reg = axd_get_decoder_flac_bitspersample_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_bitspersample(data, str); + str_append(config, str); + + reg = axd_get_decoder_flac_md5checking_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_md5checking(data, str); + str_append(config, str); +} + +static void set_flac_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define FLAC_CHANNELS_PARAM "channels" +#define FLAC_SAMPLERATE_PARAM "samplerate" +#define FLAC_BITSPERSAMPLE_PARAM "bitspersample" +#define FLAC_MD5CHECKING_PARAM "md5checking" + unsigned int reg; + int data; + + if (CMP_PARAM(config, FLAC_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, FLAC_CHANNELS_PARAM); + if (unlikely(data > 0x7 || data < 0)) + return; + reg = axd_get_decoder_flac_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_SAMPLERATE_PARAM)) { + data = PARAM_VALUE(config, FLAC_SAMPLERATE_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_flac_samplerate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_BITSPERSAMPLE_PARAM)) { + data = PARAM_VALUE(config, FLAC_BITSPERSAMPLE_PARAM); + if (unlikely(data > 0x3F || data < 0)) + return; + reg = axd_get_decoder_flac_bitspersample_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_MD5CHECKING_PARAM)) { + data = PARAM_VALUE(config, FLAC_MD5CHECKING_PARAM); + if (unlikely(data > 1 || data < 0)) + return; + reg = axd_get_decoder_flac_md5checking_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +/** Cook Config **/ +static void parse_cook_flavour(unsigned int flavour, char *config) +{ + sprintf(config, "flavour = %d\n", flavour); +} +static void get_cook_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int reg; + unsigned int data; + + reg = axd_get_decoder_cook_flavour_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_cook_flavour(data, config); +} + +static void set_cook_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define COOK_FLAVOUR_PARAM "flavour" + unsigned int reg; + int data; + + if (CMP_PARAM(config, COOK_FLAVOUR_PARAM)) { + data = PARAM_VALUE(config, COOK_FLAVOUR_PARAM); + if (unlikely(data > 29 || data < 0)) + return; + reg = axd_get_decoder_cook_flavour_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +/** WMA Config **/ +static void parse_wma_playeropt(unsigned int playeropt, char *config) +{ + sprintf(config, "playeropt = 0x%04X\n", playeropt); +} +static void parse_wma_drcsetting(unsigned int drcsetting, char *config) +{ + sprintf(config, "drcsetting = %u\n", drcsetting); +} +static void parse_wma_peakampref(unsigned int peakampref, char *config) +{ + sprintf(config, "peakampref = %u\n", peakampref); +} +static void parse_wma_rmsampref(unsigned int rmsampref, char *config) +{ + sprintf(config, "rmsampref = %u\n", rmsampref); +} +static void parse_wma_peakamptarget(unsigned int peakamptarget, char *config) +{ + sprintf(config, "peakamptarget = %u\n", peakamptarget); +} +static void parse_wma_rmsamptarget(unsigned int rmsamptarget, char *config) +{ + sprintf(config, "rmsamptarget = %u\n", rmsamptarget); +} +static void parse_wma_pcmvalidbitspersample(unsigned int pcmvalidbitspersample, + char *config) +{ + sprintf(config, "pcmvalidbitspersample = %u\n", pcmvalidbitspersample); +} +static void parse_wma_pcmcontainersize(unsigned int pcmcontainersize, + char *config) +{ + sprintf(config, "pcmcontainersize = %u\n", pcmcontainersize); +} +static void parse_wma_wmaformattag(unsigned int wmaformattag, char *config) +{ +#define WMAFORMATTAG_0x160_STR "std V1" +#define WMAFORMATTAG_0x161_STR "std V2" +#define WMAFORMATTAG_0x162_STR "Pro" +#define WMAFORMATTAG_0x163_STR "Lossless" + char *str; + + switch (wmaformattag) { + case 0x160: + str = WMAFORMATTAG_0x160_STR; + break; + case 0x161: + str = WMAFORMATTAG_0x161_STR; + break; + case 0x162: + str = WMAFORMATTAG_0x162_STR; + break; + case 0x163: + str = WMAFORMATTAG_0x163_STR; + break; + default: + return; + + } + sprintf(config, "wmaformattag = %s\n", str); +} +static void parse_wma_wmanumchannels(unsigned int wmanumchannels, char *config) +{ + sprintf(config, "wmanumchannels = %u\n", wmanumchannels); +} +static void parse_wma_wmasamplespersec(unsigned int wmasamplespersec, + char *config) +{ + sprintf(config, "wmasamplespersec = %u\n", wmasamplespersec); +} +static void parse_wma_wmaaveragebytespersec(unsigned int wmaaveragebytespersec, + char *config) +{ + sprintf(config, "wmaaveragebytespersec = %u\n", wmaaveragebytespersec); +} +static void parse_wma_wmablockalign(unsigned int wmablockalign, char *config) +{ + sprintf(config, "wmablockalign = %u\n", wmablockalign); +} +static void parse_wma_wmavalidbitspersample(unsigned int wmavalidbitspersample, + char *config) +{ + sprintf(config, "wmavalidbitspersample = %u\n", wmavalidbitspersample); +} +static void parse_wma_wmachannelmask(unsigned int wmachannelmask, char *config) +{ + sprintf(config, "wmachannelmask = %u\n", wmachannelmask); +} +static void parse_wma_wmaencodeoptions(unsigned int wmaencodeoptions, + char *config) +{ + sprintf(config, "wmaencodeoptions = 0x%04X\n", wmaencodeoptions); +} +static void get_wma_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_wma_playeropt_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_playeropt(data, config); + + reg = axd_get_decoder_wma_drcsetting_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_drcsetting(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_peakampref_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_peakampref(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_rmsampref_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_rmsampref(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_peakamptarget_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_peakamptarget(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_rmsamptarget_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_rmsamptarget(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_pcmvalidbitspersample_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_pcmvalidbitspersample(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_pcmcontainersize_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_pcmcontainersize(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmaformattag_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmaformattag(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmanumchannels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmanumchannels(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmasamplespersec_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmasamplespersec(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmaaveragebytespersec_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmaaveragebytespersec(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmablockalign_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmablockalign(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmavalidbitspersample_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmavalidbitspersample(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmachannelmask_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmachannelmask(data, str); + str_append(config, str); + + reg = axd_get_decoder_wma_wmaencodeoptions_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_wma_wmaencodeoptions(data, str); + str_append(config, str); +} + +static void set_wma_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define WMA_PLAYEROPT_PARAM "playeropt" +#define WMA_DRCSETTING_PARAM "drcsetting" +#define WMA_PEAKAMPREF_PARAM "peakampref" +#define WMA_RMSAMPREF_PARAM "rmsampref" +#define WMA_PEAKAMPTARGET_PARAM "peakamptarget" +#define WMA_RMSAMPTARGET_PARAM "rmsamptarget" +#define WMA_PCMVALIDBITSPERSAMPLE_PARAM "pcmvalidbitspersample" +#define WMA_PCMCONTAINERSIZE_PARAM "pcmcontainersize" +#define WMA_WMAFORMATTAG_PARAM "wmaformattag" +#define WMA_WMANUMCHANNELS_PARAM "wmanumchannels" +#define WMA_WMASAMPLESPERSEC_PARAM "wmasamplespersec" +#define WMA_WMAAVERAGEBYTESPERSEC_PARAM "wmaaveragebytespersec" +#define WMA_WMABLOCKALIGN_PARAM "wmablockalign" +#define WMA_WMAVALIDBITSPERSAMPLE_PARAM "wmavalidbitspersample" +#define WMA_WMACHANNELMASK_PARAM "wmachannelmask" +#define WMA_WMAENCODEOPTIONS_PARAM "wmaencodeoptions" + unsigned int reg; + int data; + + if (CMP_PARAM(config, WMA_PLAYEROPT_PARAM)) { + data = PARAM_VALUE(config, WMA_PLAYEROPT_PARAM); + if (unlikely(data > 0xFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_playeropt_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_DRCSETTING_PARAM)) { + data = PARAM_VALUE(config, WMA_DRCSETTING_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_drcsetting_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_PEAKAMPREF_PARAM)) { + data = PARAM_VALUE(config, WMA_PEAKAMPREF_PARAM); + reg = axd_get_decoder_wma_peakampref_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_RMSAMPREF_PARAM)) { + data = PARAM_VALUE(config, WMA_RMSAMPREF_PARAM); + reg = axd_get_decoder_wma_rmsampref_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_PEAKAMPTARGET_PARAM)) { + data = PARAM_VALUE(config, WMA_PEAKAMPTARGET_PARAM); + reg = axd_get_decoder_wma_peakamptarget_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_RMSAMPTARGET_PARAM)) { + data = PARAM_VALUE(config, WMA_RMSAMPTARGET_PARAM); + reg = axd_get_decoder_wma_rmsamptarget_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_PCMVALIDBITSPERSAMPLE_PARAM)) { + data = PARAM_VALUE(config, WMA_PCMVALIDBITSPERSAMPLE_PARAM); + reg = axd_get_decoder_wma_pcmvalidbitspersample_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_PCMCONTAINERSIZE_PARAM)) { + data = PARAM_VALUE(config, WMA_PCMCONTAINERSIZE_PARAM); + reg = axd_get_decoder_wma_pcmcontainersize_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMAFORMATTAG_PARAM)) { + data = PARAM_VALUE(config, WMA_WMAFORMATTAG_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_wmaformattag_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMANUMCHANNELS_PARAM)) { + data = PARAM_VALUE(config, WMA_WMANUMCHANNELS_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_wmanumchannels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMASAMPLESPERSEC_PARAM)) { + data = PARAM_VALUE(config, WMA_WMASAMPLESPERSEC_PARAM); + reg = axd_get_decoder_wma_wmasamplespersec_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMAAVERAGEBYTESPERSEC_PARAM)) { + data = PARAM_VALUE(config, WMA_WMAAVERAGEBYTESPERSEC_PARAM); + reg = axd_get_decoder_wma_wmaaveragebytespersec_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMABLOCKALIGN_PARAM)) { + data = PARAM_VALUE(config, WMA_WMABLOCKALIGN_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_wmablockalign_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMAVALIDBITSPERSAMPLE_PARAM)) { + data = PARAM_VALUE(config, WMA_WMAVALIDBITSPERSAMPLE_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_wmavalidbitspersample_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMACHANNELMASK_PARAM)) { + data = PARAM_VALUE(config, WMA_WMACHANNELMASK_PARAM); + reg = axd_get_decoder_wma_wmachannelmask_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, WMA_WMAENCODEOPTIONS_PARAM)) { + data = PARAM_VALUE(config, WMA_WMAENCODEOPTIONS_PARAM); + if (unlikely(data > 0xFFFFF || data < 0)) + return; + reg = axd_get_decoder_wma_wmaencodeoptions_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + + + +/** DDplus Config **/ + +static void parse_ddplus_config(unsigned int configreg, char *config) +{ + static const char * const dectype[] = { + "0 = Reserved", + "1 = DCV(5.1)", + "2 = DEC7.1" + }; + static const char * const output[] = {"0 = PCM", "1 = AC3"}; + + sprintf(config, "DecoderType = %s\n" + "Output = %s\n" + "OutNChans = %d\n", + dectype[configreg & 0x3], + output[(configreg & 0x10) >> 4], + (configreg >> 8) & 0xFF + ); +} + +static void +parse_ddplus_channel_order(unsigned int channels, char *config, size_t sz) +{ + static const char * const channel[] = { + "Front Left (0)", + "Centre (1)", + "Front Right (2)", + "Left Surround (3)", + "Right Surround (4)", + "L.F.E. (5)", + "Surround Back L (6)", + "Surround Back R (7)" + }; + + snprintf(config, sz, "channel-order:\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n" + "%s = %s\n", + channel[0], + channel[channels & 0xF], + channel[1], + channel[(channels >> 4) & 0xF], + channel[2], + channel[(channels >> 8) & 0xF], + channel[3], + channel[(channels >> 12) & 0xF], + channel[4], + channel[(channels >> 16) & 0xF], + channel[5], + channel[(channels >> 20) & 0xF], + channel[6], + channel[(channels >> 24) & 0xF], + channel[7], + channel[(channels >> 28) & 0xF] + ); +} + +static void get_ddplus_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ +#undef BUFF_SIZE +#define BUFF_SIZE 512 + unsigned int reg; + unsigned int data; + char str[BUFF_SIZE]; + + reg = axd_get_decoder_ddplus_config_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ddplus_config(data, config); + + reg = axd_get_decoder_ddplus_channel_order_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ddplus_channel_order(data, str, BUFF_SIZE); + str_append(config, str); + +} + +static void set_ddplus_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define DDP_DECTYPE_PARAM "DecoderType" +#define DDP_OUTPUT_PARAM "Output" +#define DDP_CHANNEL_ORDER_PARAM "channel-order" +#define DDP_OUTNCHANS_PARAM "OutNChans" + + unsigned int reg; + int data, temp[8]; + + if (CMP_PARAM(config, DDP_DECTYPE_PARAM)) { + temp[0] = PARAM_VALUE(config, DDP_DECTYPE_PARAM); + if (unlikely(temp[0] > 2 || temp[0] < 0)) + return; + reg = axd_get_decoder_ddplus_config_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) + return; + data &= ~0x3; + data |= temp[0] & 0x3; + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, DDP_OUTPUT_PARAM)) { + temp[0] = PARAM_VALUE(config, DDP_OUTPUT_PARAM); + if (unlikely(temp[0] > 1 || temp[0] < 0)) + return; + reg = axd_get_decoder_ddplus_config_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) + return; + + data &= ~0x10; + data |= (temp[0] << 4) & 0x10; + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, DDP_CHANNEL_ORDER_PARAM)) { + sscanf(config, "channel-order=%d,%d,%d,%d,%d,%d,%d,%d", + &temp[0], &temp[1], &temp[2], &temp[3], + &temp[4], &temp[5], &temp[6], &temp[7]); + data = ((temp[0] & 0xF) << 0) | + ((temp[1] & 0xF) << 4) | + ((temp[2] & 0xF) << 8) | + ((temp[3] & 0xF) << 12) | + ((temp[4] & 0xF) << 16) | + ((temp[5] & 0xF) << 20) | + ((temp[6] & 0xF) << 24) | + ((temp[7] & 0xF) << 28); + reg = axd_get_decoder_ddplus_channel_order_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, DDP_OUTNCHANS_PARAM)) { + temp[0] = PARAM_VALUE(config, DDP_OUTNCHANS_PARAM); + if (unlikely(temp[0] > 8 || temp[0] < 0)) + return; + reg = axd_get_decoder_ddplus_config_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) + return; + data &= ~0xFF00; + data |= (temp[0] << 8); + axd_write_reg(cmd, reg, data); + } +} + +/** ALAC Config **/ +static void parse_alac_channels(uint8_t numchannels, char *config) +{ + sprintf(config, "channels = %u\n", numchannels); +} +static void parse_alac_depth(uint8_t bitdepth, char *config) +{ + sprintf(config, "depth = %u\n", bitdepth); +} +static void parse_alac_samplerate(uint32_t samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} +static void parse_alac_framelength(uint32_t framelength, char *config) +{ + sprintf(config, "framelength = %u\n", framelength); +} +static void parse_alac_maxframebytes(uint32_t maxframebytes, char *config) +{ + sprintf(config, "maxframebytes = %u\n", maxframebytes); +} +static void parse_alac_avgbitrate(uint32_t avgbitrate, char *config) +{ + sprintf(config, "avgbitrate = %u\n", avgbitrate); +} +static void get_alac_config(struct axd_cmd *cmd, + unsigned int pipe, char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_alac_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_channels(data, config); + + reg = axd_get_decoder_alac_depth_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_depth(data, str); + str_append(config, str); + + reg = axd_get_decoder_alac_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_samplerate(data, str); + str_append(config, str); + + reg = axd_get_decoder_alac_framelength_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_framelength(data, str); + str_append(config, str); + + reg = axd_get_decoder_alac_maxframebytes_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_maxframebytes(data, str); + str_append(config, str); + + reg = axd_get_decoder_alac_avgbitrate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_avgbitrate(data, str); + str_append(config, str); +} +static void set_alac_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define ALAC_CHANNELS_PARAM "channels" +#define ALAC_DEPTH_PARAM "depth" +#define ALAC_SAMPLERATE_PARAM "samplerate" +#define ALAC_FRAMELENGTH_PARAM "framelength" +#define ALAC_MAXFRAMEBYTES_PARAM "maxframebytes" +#define ALAC_AVGBITRATE_PARAM "avgbitrate" + + unsigned int reg; + int data; + + if (CMP_PARAM(config, ALAC_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, ALAC_CHANNELS_PARAM); + switch (data) { + case 1: + case 2: + /* TSTODO Add multichannel support if we can. */ + break; + default: + return; + } + reg = axd_get_decoder_alac_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_DEPTH_PARAM)) { + data = PARAM_VALUE(config, ALAC_DEPTH_PARAM); + switch (data) { + case 16: + case 20: /* TSTODO test vectors for this */ + case 24: + case 32: + break; + default: + return; + } + reg = axd_get_decoder_alac_depth_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_SAMPLERATE_PARAM)) { + data = PARAM_VALUE(config, ALAC_SAMPLERATE_PARAM); + reg = axd_get_decoder_alac_samplerate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_FRAMELENGTH_PARAM)) { + data = PARAM_VALUE(config, ALAC_FRAMELENGTH_PARAM); + /* TSTODO sanitize */ + reg = axd_get_decoder_alac_framelength_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_MAXFRAMEBYTES_PARAM)) { + data = PARAM_VALUE(config, ALAC_MAXFRAMEBYTES_PARAM); + /* TSTODO sanitize */ + reg = axd_get_decoder_alac_maxframebytes_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_AVGBITRATE_PARAM)) { + data = PARAM_VALUE(config, ALAC_AVGBITRATE_PARAM); + /* TSTODO sanitize */ + reg = axd_get_decoder_alac_avgbitrate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +/** SBC Config **/ +static void parse_sbc_samplerate(uint8_t samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} +static void parse_sbc_audiomode(uint8_t audiomode, char *config) +{ + sprintf(config, "audiomode = %u\n", audiomode); +} +static void parse_sbc_blocks(uint8_t blocks, char *config) +{ + sprintf(config, "blocks = %u\n", blocks); +} +static void parse_sbc_subbands(uint8_t subbands, char *config) +{ + sprintf(config, "subbands = %u\n", subbands); +} +static void parse_sbc_bitpool(uint8_t bitpool, char *config) +{ + sprintf(config, "bitpool = %u\n", bitpool); +} +static void parse_sbc_allocationmode(uint8_t allocationmode, char *config) +{ + sprintf(config, "allocationmode = %u\n", allocationmode); +} +static void get_sbc_config(struct axd_cmd *cmd, unsigned int pipe, char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_decoder_sbc_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_sbc_samplerate(data, config); + + reg = axd_get_decoder_sbc_audiomode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_sbc_audiomode(data, str); + str_append(config, str); + + reg = axd_get_decoder_sbc_blocks_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_sbc_blocks(data, str); + str_append(config, str); + + reg = axd_get_decoder_sbc_subbands_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_sbc_subbands(data, str); + str_append(config, str); + + reg = axd_get_decoder_sbc_bitpool_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_sbc_bitpool(data, str); + str_append(config, str); + + reg = axd_get_decoder_sbc_allocationmode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_sbc_allocationmode(data, str); + str_append(config, str); +} + +/** MS11 Config **/ +static void parse_ms11_mode(uint32_t mode, char *config) +{ + static const char * const typestr[] = { + "DDC", + "DDT", + "External PCM" + }; + int input_type = (mode >> 0) & 0x3; + int dual_input = (mode >> 2) & 0x1; + int dolby_volume_258 = (mode >> 3) & 0x1; + int ms10_mode = (mode >> 4) & 0x1; + int main_only = (mode >> 5) & 0x1; + + sprintf(config, "input_type = %s\n" + "dual_input = %d\n" + "dolby_volume_258 = %d\n" + "ms10_mode = %d\n" + "main_only = %d\n", + typestr[input_type], dual_input, dolby_volume_258, + ms10_mode, main_only); +} +static void parse_ms11_common_config0(uint32_t common_config0, char *config) +{ + int drc_cut_fac_6ch = (common_config0 >> 0) & 0xFF; + int drc_boost_fac_6ch = (common_config0 >> 8) & 0xFF; + int drc_cut_fac_2ch = (common_config0 >> 16) & 0xFF; + int drc_boost_fac_2ch = (common_config0 >> 24) & 0xFF; + + sprintf(config, "drc_cut_fac_6ch = %d\n" + "drc_boost_fac_6ch = %d\n" + "drc_cut_fac_2ch = %d\n" + "drc_boost_fac_2ch = %d\n", + drc_cut_fac_6ch, drc_boost_fac_6ch, + drc_cut_fac_2ch, drc_boost_fac_2ch); +} +static void parse_ms11_common_config1(uint32_t common_config1, char *config) +{ + int downmix_type = (common_config1 >> 0) & 0x3; + char *drc_mode = (common_config1 & 0x4) ? "RF mode" : "Line mode"; + int dual_mono = (common_config1 >> 3) & 0x3; + int output_multichannel_enable = (common_config1 >> 5) & 0x1; + int associated_audio_mixing = (common_config1 >> 6) & 0x1; + int16_t user_balance_adjustment = (common_config1 >> 16) & 0xFFFF; + + sprintf(config, "downmixtype = %d\n" + "drc_mode = %s\n" + "dual_mono = %d\n" + "output_multichannel_enable = %d\n" + "associated_audio_mixing = %d\n" + "user_balance_adjustment = %d\n", + downmix_type, drc_mode, dual_mono, output_multichannel_enable, + associated_audio_mixing, user_balance_adjustment); +} +static void parse_ms11_ddt_config0(uint32_t ddt_config0, char *config) +{ + int ddt_default_dialnorm = (ddt_config0 >> 0) & 0xFF; + int ddt_transport_format = (ddt_config0 >> 8) & 0xFF; + int ddt_mixing_mode = (ddt_config0 >> 16) & 0x1; + + sprintf(config, "ddt_default_dialnorm = %d\n" + "ddt_transport_format = %d\n" + "ddt_mixing_mode = %d\n", + ddt_default_dialnorm, ddt_transport_format, ddt_mixing_mode); +} +static void parse_ms11_ddc_config0(uint32_t ddc_config0, char *config) +{ + int ddc_associated_substream = (ddc_config0 >> 0) & 0xFF; + int ddc_out_mode = (ddc_config0 >> 8) & 0xFF; + int ddc_out_lfe = (ddc_config0 >> 16) & 0x1; + + sprintf(config, "ddc_associated_substream = %d\n" + "ddc_out_mode = %d\n" + "ddc_out_lfe = %d\n", + ddc_associated_substream, ddc_out_mode, ddc_out_lfe); +} +static void parse_ms11_ext_pcm_config0(uint32_t ext_pcm_config0, char *config) +{ + int ext_pcm_number_in_samples = (ext_pcm_config0 >> 0) & 0xFFFF; + int ext_pcm_audio_coding_mode = (ext_pcm_config0 >> 16) & 0x3; + int ext_pcm_lfe_present = (ext_pcm_config0 >> 18) & 0x1; + int ext_pcm_dsur_mode = (ext_pcm_config0 >> 19) & 0x1; + + sprintf(config, "ext_pcm_number_in_samples = %d\n" + "ext_pcm_audio_coding_mode = %d\n" + "ext_pcm_lfe_present = %d\n" + "ext_pcm_dsur_mode = %d\n", + ext_pcm_number_in_samples, ext_pcm_audio_coding_mode, + ext_pcm_lfe_present, ext_pcm_dsur_mode); +} +static void get_ms11_config(struct axd_cmd *cmd, + unsigned int pipe, char *config) +{ + unsigned int reg; + unsigned int data; + unsigned int input_type; + char str[164]; + + reg = axd_get_decoder_ms11_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ms11_mode(data, config); + + input_type = data & 0x3; + + reg = axd_get_decoder_ms11_common_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ms11_common_config0(data, str); + str_append(config, str); + + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ms11_common_config1(data, str); + str_append(config, str); + + switch (input_type) { + case 0: /* DDC */ + reg = axd_get_decoder_ms11_ddc_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ms11_ddc_config0(data, str); + str_append(config, str); + break; + case 1: /* DDT */ + reg = axd_get_decoder_ms11_ddt_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ms11_ddt_config0(data, str); + str_append(config, str); + break; + case 2: /* EXTERNAL PCM */ + reg = axd_get_decoder_ms11_ext_pcm_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_ms11_ext_pcm_config0(data, str); + str_append(config, str); + break; + default: + return; + } +} +static void set_ms11_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define MS11_INPUT_TYPE_PARAM "input_type" +#define MS11_DUAL_INPUT_PARAM "dual_input" +#define MS11_DOLBY_VOLUME_258_PARAM "dolby_volume_258" +#define MS11_MS10_MODE_PARAM "ms10_mode" +#define MS11_MAIN_ONLY_PARAM "main_only" +#define MS11_DRC_CUT_FAC_6CH_PARAM "drc_cut_fac_6ch" +#define MS11_DRC_BOOST_FAC_6CH_PARAM "drc_boost_fac_6ch" +#define MS11_DRC_CUT_FAC_2CH_PARAM "drc_cut_fac_2ch" +#define MS11_DRC_BOOST_FAC_2CH_PARAM "drc_boost_fac_2ch" +#define MS11_DOWNMIX_TYPE_PARAM "downmix_type" +#define MS11_DRC_MODE_PARAM "drc_mode" +#define MS11_DUAL_MONO_PARAM "dual_mono" +#define MS11_OUTPUT_MULTICHANNEL_ENABLE_PARAM "output_multichannel_enable" +#define MS11_ASSOCIATED_AUDIO_MIXING_PARAM "associated_audio_mixing" +#define MS11_USER_BALANCE_ADJUSTMENT_PARAM "user_balance_adjustment" +#define MS11_DDT_DEFAULT_DIALNORM_PARAM "ddt_default_dialnorm" +#define MS11_DDT_TRANSPORT_FORMAT_PARAM "ddt_transport_format" +#define MS11_DDT_MIXING_MODE_PARAM "ddt_mixing_mode" +#define MS11_DDC_ASSOCIATED_SUBSTREAM_PARAM "ddc_associated_substream" +#define MS11_DDC_OUT_MODE_PARAM "ddc_out_mode" +#define MS11_DDC_OUT_LFE_PARAM "ddc_out_lfe" +#define MS11_EXT_PCM_NUMBER_IN_SAMPLES_PARAM "ext_pcm_number_in_samples" +#define MS11_EXT_PCM_AUDIO_CODING_MODE_PARAM "ext_pcm_audio_coding_mode" +#define MS11_EXT_PCM_LFE_PRESENT_PARAM "ext_pcm_lfe_present" +#define MS11_EXT_PCM_DOLBY_SURROUND_MODE_PARAM "ext_pcm_dsur_mode" + + unsigned int reg; + int data; + int temp = 0; + + if (CMP_PARAM(config, MS11_INPUT_TYPE_PARAM)) { + data = PARAM_VALUE(config, MS11_INPUT_TYPE_PARAM); + reg = axd_get_decoder_ms11_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x3 << 0); + temp |= (data & 0x3) << 0; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DUAL_INPUT_PARAM)) { + data = PARAM_VALUE(config, MS11_DUAL_INPUT_PARAM); + reg = axd_get_decoder_ms11_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 2); + temp |= (!!data) << 2; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DOLBY_VOLUME_258_PARAM)) { + data = PARAM_VALUE(config, MS11_DOLBY_VOLUME_258_PARAM); + reg = axd_get_decoder_ms11_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 3); + temp |= (!!data) << 3; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_MS10_MODE_PARAM)) { + data = PARAM_VALUE(config, MS11_MS10_MODE_PARAM); + reg = axd_get_decoder_ms11_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 4); + temp |= (!!data) << 4; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_MAIN_ONLY_PARAM)) { + data = PARAM_VALUE(config, MS11_MAIN_ONLY_PARAM); + reg = axd_get_decoder_ms11_mode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 5); + temp |= (!!data) << 5; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DRC_CUT_FAC_6CH_PARAM)) { + data = PARAM_VALUE(config, MS11_DRC_CUT_FAC_6CH_PARAM); + reg = axd_get_decoder_ms11_common_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 0); + temp |= (data & 0xFF) << 0; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DRC_BOOST_FAC_6CH_PARAM)) { + data = PARAM_VALUE(config, MS11_DRC_BOOST_FAC_6CH_PARAM); + reg = axd_get_decoder_ms11_common_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 8); + temp |= (data & 0xFF) << 8; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DRC_CUT_FAC_2CH_PARAM)) { + data = PARAM_VALUE(config, MS11_DRC_CUT_FAC_2CH_PARAM); + reg = axd_get_decoder_ms11_common_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 16); + temp |= (data & 0xFF) << 16; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DRC_BOOST_FAC_2CH_PARAM)) { + data = PARAM_VALUE(config, MS11_DRC_BOOST_FAC_2CH_PARAM); + reg = axd_get_decoder_ms11_common_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 24); + temp |= (data & 0xFF) << 24; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DOWNMIX_TYPE_PARAM)) { + data = PARAM_VALUE(config, MS11_DOWNMIX_TYPE_PARAM); + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x3 << 0); + temp |= (data & 0x3) << 0; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DRC_MODE_PARAM)) { + data = PARAM_VALUE(config, MS11_DRC_MODE_PARAM); + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 2); + temp |= (!!data) << 2; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DUAL_MONO_PARAM)) { + data = PARAM_VALUE(config, MS11_DUAL_MONO_PARAM); + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x3 << 3); + temp |= (data & 0x3) << 3; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_OUTPUT_MULTICHANNEL_ENABLE_PARAM)) { + data = PARAM_VALUE(config, + MS11_OUTPUT_MULTICHANNEL_ENABLE_PARAM); + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 5); + temp |= (!!data) << 5; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_ASSOCIATED_AUDIO_MIXING_PARAM)) { + data = PARAM_VALUE(config, MS11_ASSOCIATED_AUDIO_MIXING_PARAM); + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 6); + temp |= (!!data) << 6; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_USER_BALANCE_ADJUSTMENT_PARAM)) { + data = PARAM_VALUE(config, MS11_USER_BALANCE_ADJUSTMENT_PARAM); + reg = axd_get_decoder_ms11_common_config1_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFFFF << 16); + temp |= (data & 0xFFFF) << 16; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DDT_DEFAULT_DIALNORM_PARAM)) { + data = PARAM_VALUE(config, MS11_DDT_DEFAULT_DIALNORM_PARAM); + reg = axd_get_decoder_ms11_ddt_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 0); + temp |= (data & 0xFF) << 0; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DDT_TRANSPORT_FORMAT_PARAM)) { + data = PARAM_VALUE(config, MS11_DDT_TRANSPORT_FORMAT_PARAM); + reg = axd_get_decoder_ms11_ddt_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 8); + temp |= (data & 0xFF) << 8; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DDT_MIXING_MODE_PARAM)) { + data = PARAM_VALUE(config, MS11_DDT_MIXING_MODE_PARAM); + reg = axd_get_decoder_ms11_ddt_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 16); + temp |= (!!data) << 16; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DDC_ASSOCIATED_SUBSTREAM_PARAM)) { + data = PARAM_VALUE(config, MS11_DDC_ASSOCIATED_SUBSTREAM_PARAM); + reg = axd_get_decoder_ms11_ddc_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 0); + temp |= (data & 0xFF) << 0; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DDC_OUT_MODE_PARAM)) { + data = PARAM_VALUE(config, MS11_DDC_OUT_MODE_PARAM); + reg = axd_get_decoder_ms11_ddc_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFF << 8); + temp |= (data & 0xFF) << 8; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_DDC_OUT_LFE_PARAM)) { + data = PARAM_VALUE(config, MS11_DDC_OUT_LFE_PARAM); + reg = axd_get_decoder_ms11_ddc_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 16); + temp |= (!!data) << 16; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_EXT_PCM_NUMBER_IN_SAMPLES_PARAM)) { + data = PARAM_VALUE(config, + MS11_EXT_PCM_NUMBER_IN_SAMPLES_PARAM); + reg = axd_get_decoder_ms11_ext_pcm_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0xFFFF << 0); + temp |= (data & 0xFFFF) << 0; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_EXT_PCM_AUDIO_CODING_MODE_PARAM)) { + data = PARAM_VALUE(config, + MS11_EXT_PCM_AUDIO_CODING_MODE_PARAM); + reg = axd_get_decoder_ms11_ext_pcm_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x3 << 16); + temp |= (data & 0x3) << 16; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_EXT_PCM_LFE_PRESENT_PARAM)) { + data = PARAM_VALUE(config, MS11_EXT_PCM_LFE_PRESENT_PARAM); + reg = axd_get_decoder_ms11_ext_pcm_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 18); + temp |= (!!data) << 18; + axd_write_reg(cmd, reg, temp); + } else if (CMP_PARAM(config, MS11_EXT_PCM_DOLBY_SURROUND_MODE_PARAM)) { + data = PARAM_VALUE(config, + MS11_EXT_PCM_DOLBY_SURROUND_MODE_PARAM); + reg = axd_get_decoder_ms11_ext_pcm_config0_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &temp)) + return; + temp &= ~(0x1 << 19); + temp |= (!!data) << 19; + axd_write_reg(cmd, reg, temp); + } +} + +void axd_cmd_input_get_decoder_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int codec = axd_get_input_codec_number(cmd, pipe); + + switch (codec) { + case 0: + get_pcm_config(cmd, pipe, config); + return; + case 1: + get_mpeg_config(cmd, pipe, config); + return; + case 2: + get_ac3_config(cmd, pipe, config); + return; + case 3: + get_aac_config(cmd, pipe, config); + return; + case 4: + get_ogg_config(cmd, pipe, config); + return; + case 5: + get_flac_config(cmd, pipe, config); + return; + case 6: + get_cook_config(cmd, pipe, config); + return; + case 7: + get_wma_config(cmd, pipe, config); + return; + case 8: + get_ddplus_config(cmd, pipe, config); + return; + case 11: + get_alac_config(cmd, pipe, config); + return; + case 12: + get_ms11_config(cmd, pipe, config); + return; + case 13: + get_sbc_config(cmd, pipe, config); + return; + default: + *config = '\0'; + return; + } +} + +void axd_cmd_input_set_decoder_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ + unsigned int codec = axd_get_input_codec_number(cmd, pipe); + + switch (codec) { + default: + case 0: + set_pcm_config(cmd, pipe, config); + return; + case 1: + set_mpeg_config(cmd, pipe, config); + return; + case 2: + set_ac3_config(cmd, pipe, config); + return; + case 3: + set_aac_config(cmd, pipe, config); + return; + case 4: + set_ogg_config(cmd, pipe, config); + return; + case 5: + set_flac_config(cmd, pipe, config); + return; + case 6: + set_cook_config(cmd, pipe, config); + return; + case 7: + set_wma_config(cmd, pipe, config); + return; + case 8: + set_ddplus_config(cmd, pipe, config); + return; + case 11: + set_alac_config(cmd, pipe, config); + return; + case 12: + set_ms11_config(cmd, pipe, config); + return; + case 13: + /* No configuration needed; autoconfigured by stream */ + return; + } +} diff --git a/drivers/char/axd/axd_cmds_encoder_config.c b/drivers/char/axd/axd_cmds_encoder_config.c new file mode 100644 index 000000000000..04f8ca258f2f --- /dev/null +++ b/drivers/char/axd/axd_cmds_encoder_config.c @@ -0,0 +1,519 @@ +/* + * 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 Commands API - Encoder Configuration functions + */ +#include "axd_cmds.h" +#include "axd_cmds_internal.h" +#include "axd_cmds_codec_internal.h" +#include "axd_sysfs.h" + +static void set_pcm_config(struct axd_cmd *cmd, + unsigned int pipe, const char *config) +{ + unsigned int reg; + int data; + + if (CMP_PARAM(config, PCM_BITSPERSAMPLE_PARAM)) { + data = PARAM_VALUE(config, PCM_BITSPERSAMPLE_PARAM); + switch (data) { + case 8: + case 16: + case 24: + case 32: + break; + default: + return; + } + reg = axd_get_encoder_pcm_bitspersample_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +static void get_pcm_config(struct axd_cmd *cmd, unsigned int pipe, char *config) +{ + unsigned int reg; + unsigned int data; + + reg = axd_get_encoder_pcm_bitspersample_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_pcm_bitspersample(data, config); +} + +/** FLAC Config **/ +static void parse_flac_channels(unsigned int channels, char *config) +{ + sprintf(config, "channels = %u\n", channels); +} +static void parse_flac_samplerate(unsigned int samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} +static void parse_flac_bitspersample(unsigned int bitspersample, char *config) +{ + sprintf(config, "bitspersample = %u\n", bitspersample); +} +static void parse_flac_totalsamples(unsigned int totalsamples, char *config) +{ + sprintf(config, "totalsamples = %u\n", totalsamples); +} +static void parse_flac_blocksize(unsigned int blocksize, char *config) +{ + sprintf(config, "blocksize = %u\n", blocksize); +} +static void parse_flac_domidsidestereo(unsigned int domidsidestereo, + char *config) +{ + sprintf(config, "domidsidestereo = %u\n", domidsidestereo); +} +static void parse_flac_loosemidsidestereo(unsigned int loosemidsidestereo, + char *config) +{ + sprintf(config, "loosemidsidestereo = %u\n", loosemidsidestereo); +} +static void parse_flac_doexhaustivemodelsearch( + unsigned int doexhaustivemodelsearch, char *config) +{ + sprintf(config, "doexhaustivemodelsearch = %u\n", + doexhaustivemodelsearch); +} +static void parse_flac_minresidualpartitionorder( + unsigned int minresidualpartitionorder, char *config) +{ + sprintf(config, "minresidualpartitionorder = %u\n", + minresidualpartitionorder); +} +static void parse_flac_maxresidualpartitionorder( + unsigned int maxresidualpartitionorder, char *config) +{ + sprintf(config, "maxresidualpartitionorder = %u\n", + maxresidualpartitionorder); +} +static void parse_flac_framebytes(unsigned int framebytes, char *config) +{ + sprintf(config, "framebytes = %u\n", framebytes); +} +static void parse_flac_framecount(unsigned int framecount, char *config) +{ + sprintf(config, "framecount = %u\n", framecount); +} +static void parse_flac_samplecount(unsigned int samplecount, char *config) +{ + sprintf(config, "samplecount = %u\n", samplecount); +} +static void parse_flac_bytecount(unsigned int bytecount, char *config) +{ + sprintf(config, "bytecount = %u\n", bytecount); +} +static void get_flac_config(struct axd_cmd *cmd, + unsigned int pipe, char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_encoder_flac_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_channels(data, config); + + reg = axd_get_encoder_flac_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_samplerate(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_bitspersample_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_bitspersample(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_totalsamples_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_totalsamples(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_blocksize_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_blocksize(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_domidsidestereo_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_domidsidestereo(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_loosemidsidestereo_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_loosemidsidestereo(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_doexhaustivemodelsearch_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_doexhaustivemodelsearch(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_minresidualpartitionorder_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_minresidualpartitionorder(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_maxresidualpartitionorder_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_maxresidualpartitionorder(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_framebytes_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_framebytes(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_framecount_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_framecount(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_samplecount_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_samplecount(data, str); + str_append(config, str); + + reg = axd_get_encoder_flac_bytecount_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_flac_bytecount(data, str); + str_append(config, str); +} + +static void set_flac_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define FLAC_CHANNELS_PARAM "channels" +#define FLAC_SAMPLERATE_PARAM "samplerate" +#define FLAC_BITSPERSAMPLE_PARAM "bitspersample" +#define FLAC_TOTALSAMPLES_PARAM "totalsamples" +#define FLAC_BLOCKSIZE_PARAM "blocksize" +#define FLAC_DOMIDSIDESTEREO_PARAM "domidsidestereo" +#define FLAC_LOOSEMIDSIDESTEREO_PARAM "loosemidsidestereo" +#define FLAC_DOEXHAUSTIVEMODELSEARCH_PARAM "doexhaustivemodelsearch" +#define FLAC_MINRESIDUALPARTITIONORDER_PARAM "minresidualpartitionorder" +#define FLAC_MAXRESIDUALPARTITIONORDER_PARAM "maxresidualpartitionorder" +#define FLAC_FRAMEBYTES_PARAM "frameBytes" +#define FLAC_FRAMECOUNT_PARAM "frameCount" +#define FLAC_SAMPLECOUNT_PARAM "sampleCount" +#define FLAC_BYTECOUNT_PARAM "byteCount" + unsigned int reg; + int data; + + if (CMP_PARAM(config, FLAC_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, FLAC_CHANNELS_PARAM); + if (unlikely(data < 1 || data > 8)) + return; + reg = axd_get_encoder_flac_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_SAMPLERATE_PARAM)) { + data = PARAM_VALUE(config, FLAC_SAMPLERATE_PARAM); + if (unlikely(data < 1 || data > 655350)) + return; + reg = axd_get_encoder_flac_samplerate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_BITSPERSAMPLE_PARAM)) { + data = PARAM_VALUE(config, FLAC_BITSPERSAMPLE_PARAM); + if (unlikely(data < 8 || data > 24)) + return; + reg = axd_get_encoder_flac_bitspersample_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_TOTALSAMPLES_PARAM)) { + data = PARAM_VALUE(config, FLAC_TOTALSAMPLES_PARAM); + reg = axd_get_encoder_flac_totalsamples_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_BLOCKSIZE_PARAM)) { + data = PARAM_VALUE(config, FLAC_BLOCKSIZE_PARAM); + if (unlikely(data != 4096)) + return; + reg = axd_get_encoder_flac_blocksize_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_DOMIDSIDESTEREO_PARAM)) { + data = PARAM_VALUE(config, FLAC_DOMIDSIDESTEREO_PARAM); + if (unlikely(data < 0 || data > 1)) + return; + reg = axd_get_encoder_flac_domidsidestereo_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_LOOSEMIDSIDESTEREO_PARAM)) { + data = PARAM_VALUE(config, FLAC_LOOSEMIDSIDESTEREO_PARAM); + if (unlikely(data < 0 || data > 1)) + return; + reg = axd_get_encoder_flac_loosemidsidestereo_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_DOEXHAUSTIVEMODELSEARCH_PARAM)) { + data = PARAM_VALUE(config, FLAC_DOEXHAUSTIVEMODELSEARCH_PARAM); + if (unlikely(data < 0 || data > 1)) + return; + reg = axd_get_encoder_flac_doexhaustivemodelsearch_reg(cmd, + pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_MINRESIDUALPARTITIONORDER_PARAM)) { + data = PARAM_VALUE(config, + FLAC_MINRESIDUALPARTITIONORDER_PARAM); + if (unlikely(data < 0 || data > 6)) /* Actual upper limit: 16 */ + return; + reg = axd_get_encoder_flac_minresidualpartitionorder_reg(cmd, + pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_MAXRESIDUALPARTITIONORDER_PARAM)) { + data = PARAM_VALUE(config, + FLAC_MAXRESIDUALPARTITIONORDER_PARAM); + if (unlikely(data < 0 || data > 6)) /* Actual upper limit: 16 */ + return; + reg = axd_get_encoder_flac_maxresidualpartitionorder_reg(cmd, + pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, FLAC_FRAMEBYTES_PARAM)) { + /* Read-only */ + return; + } else if (CMP_PARAM(config, FLAC_FRAMECOUNT_PARAM)) { + /* Read-only */ + return; + } else if (CMP_PARAM(config, FLAC_SAMPLECOUNT_PARAM)) { + /* Read-only */ + return; + } else if (CMP_PARAM(config, FLAC_BYTECOUNT_PARAM)) { + /* Read-only */ + return; + } + +} + +/** ALAC Config **/ +static void parse_alac_channels(unsigned int channels, char *config) +{ + sprintf(config, "channels = %u\n", channels); +} +static void parse_alac_depth(unsigned int depth, char *config) +{ + sprintf(config, "depth = %u\n", depth); +} +static void parse_alac_samplerate(unsigned int samplerate, char *config) +{ + sprintf(config, "samplerate = %u\n", samplerate); +} +static void parse_alac_framelength(unsigned int framelength, char *config) +{ + sprintf(config, "framelength = %u\n", framelength); +} +static void parse_alac_maxframebytes(unsigned int maxframebytes, char *config) +{ + sprintf(config, "maxframebytes = %u\n", maxframebytes); +} +static void parse_alac_avgbitrate(unsigned int avgbitrate, char *config) +{ + sprintf(config, "avgbitrate = %u\n", avgbitrate); +} +static void parse_alac_fastmode(unsigned int fastmode, char *config) +{ + sprintf(config, "fastmode = %u\n", fastmode); +} + +static void get_alac_config(struct axd_cmd *cmd, + unsigned int pipe, char *config) +{ + unsigned int reg; + unsigned int data; + char str[32]; + + reg = axd_get_encoder_alac_channels_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_channels(data, config); + + reg = axd_get_encoder_alac_depth_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_depth(data, str); + str_append(config, str); + + reg = axd_get_encoder_alac_samplerate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_samplerate(data, str); + str_append(config, str); + + reg = axd_get_encoder_alac_framelength_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_framelength(data, str); + str_append(config, str); + + reg = axd_get_encoder_alac_maxframebytes_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_maxframebytes(data, str); + str_append(config, str); + + reg = axd_get_encoder_alac_avgbitrate_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_avgbitrate(data, str); + str_append(config, str); + + reg = axd_get_encoder_alac_fastmode_reg(cmd, pipe); + if (axd_read_reg(cmd, reg, &data)) { + print_timeout_msg(config); + return; + } + parse_alac_fastmode(data, str); + str_append(config, str); +} + +static void set_alac_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ +#define ALAC_CHANNELS_PARAM "channels" +#define ALAC_DEPTH_PARAM "depth" +#define ALAC_SAMPLERATE_PARAM "samplerate" +#define ALAC_FRAMELENGTH_PARAM "framelength" +#define ALAC_MAXFRAMEBYTES_PARAM "maxframebytes" +#define ALAC_AVGBITRATE_PARAM "avgbitrate" +#define ALAC_FASTMODE_PARAM "fastmode" + unsigned int reg; + int data; + + if (CMP_PARAM(config, ALAC_CHANNELS_PARAM)) { + data = PARAM_VALUE(config, ALAC_CHANNELS_PARAM); + if (unlikely(data < 1 || data > 8)) + return; + reg = axd_get_encoder_alac_channels_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_DEPTH_PARAM)) { + data = PARAM_VALUE(config, ALAC_DEPTH_PARAM); + if (unlikely(data < 8 || data > 32)) + return; + reg = axd_get_encoder_alac_depth_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_SAMPLERATE_PARAM)) { + data = PARAM_VALUE(config, ALAC_SAMPLERATE_PARAM); + /* TSTODO validate */ + reg = axd_get_encoder_alac_samplerate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_FRAMELENGTH_PARAM)) { + data = PARAM_VALUE(config, ALAC_FRAMELENGTH_PARAM); + /* TSTODO validate */ + reg = axd_get_encoder_alac_framelength_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_MAXFRAMEBYTES_PARAM)) { + data = PARAM_VALUE(config, ALAC_MAXFRAMEBYTES_PARAM); + /* TSTODO validate */ + reg = axd_get_encoder_alac_maxframebytes_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_AVGBITRATE_PARAM)) { + data = PARAM_VALUE(config, ALAC_AVGBITRATE_PARAM); + /* TSTODO validate */ + reg = axd_get_encoder_alac_avgbitrate_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } else if (CMP_PARAM(config, ALAC_FASTMODE_PARAM)) { + data = PARAM_VALUE(config, ALAC_FASTMODE_PARAM); + if (unlikely(data < 0 || data > 1)) + return; + reg = axd_get_encoder_alac_fastmode_reg(cmd, pipe); + axd_write_reg(cmd, reg, data); + } +} + +void axd_cmd_output_get_encoder_config(struct axd_cmd *cmd, unsigned int pipe, + char *config) +{ + unsigned int codec = axd_get_output_codec_number(cmd, pipe); + + switch (codec) { + case 0: + get_pcm_config(cmd, pipe, config); + return; + case 5: + get_flac_config(cmd, pipe, config); + return; + case 11: + get_alac_config(cmd, pipe, config); + return; + default: + *config = '\0'; + return; + } +} + +void axd_cmd_output_set_encoder_config(struct axd_cmd *cmd, unsigned int pipe, + const char *config) +{ + unsigned int codec = axd_get_output_codec_number(cmd, pipe); + + switch (codec) { + default: + case 0: + set_pcm_config(cmd, pipe, config); + return; + case 5: + set_flac_config(cmd, pipe, config); + return; + case 11: + set_alac_config(cmd, pipe, config); + return; + } +} diff --git a/drivers/char/axd/axd_cmds_info.c b/drivers/char/axd/axd_cmds_info.c new file mode 100644 index 000000000000..befb3b5e2534 --- /dev/null +++ b/drivers/char/axd/axd_cmds_info.c @@ -0,0 +1,1409 @@ +/* + * 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 Commands API - Info functions. + */ +#include "axd_cmds.h" +#include "axd_cmds_internal.h" + +/* static functions */ +/* Fills @source with the source as set in the bit map @bits */ +static void parse_source(unsigned int bits, char *source) +{ +#define SOURCE_PIPE_STR "pipe\n" +#define SOURCE_AUX_STR "aux\n" + + switch (bits) { + case 0: + strcpy(source, SOURCE_PIPE_STR); + source += sizeof(SOURCE_PIPE_STR); + source--; /* terminating null */ + break; + case 1: + strcpy(source, SOURCE_AUX_STR); + source += sizeof(SOURCE_AUX_STR); + source--; /* terminating null */ + break; + default: + break; + } + *source = '\0'; +} + +/* Fills @sink with the sink as set in the bit map @bits */ +static void parse_sink(unsigned int bits, char *sink) +{ +#define SINK_PIPE_STR "pipe\n" +#define SINK_I2S_STR "i2s\n" + + switch (bits) { + case 0: + strcpy(sink, SINK_PIPE_STR); + sink += sizeof(SINK_PIPE_STR); + sink--; /* terminating null */ + break; + case 1: + strcpy(sink, SINK_I2S_STR); + sink += sizeof(SINK_I2S_STR); + sink--; /* terminating null */ + break; + default: + break; + } + *sink = '\0'; +} + +/* Fills @codec with the list of codecs as set in the bit map @bits */ +static void parse_codec_by_bit(unsigned int bits, char *codecs) +{ +#define CODEC_BIT0_MASK (1 << 0) +#define CODEC_BIT0_STR "PCM Pass through\n" +#define CODEC_BIT1_MASK (1 << 1) +#define CODEC_BIT1_STR "MPEG (MP2/3)\n" +#define CODEC_BIT2_MASK (1 << 2) +#define CODEC_BIT2_STR "Dolby AC3\n" +#define CODEC_BIT3_MASK (1 << 3) +#define CODEC_BIT3_STR "AAC\n" +#define CODEC_BIT4_MASK (1 << 4) +#define CODEC_BIT4_STR "Ogg Vorbis\n" +#define CODEC_BIT5_MASK (1 << 5) +#define CODEC_BIT5_STR "FLAC\n" +#define CODEC_BIT6_MASK (1 << 6) +#define CODEC_BIT6_STR "Cook\n" +#define CODEC_BIT7_MASK (1 << 7) +#define CODEC_BIT7_STR "WMA\n" +#define CODEC_BIT8_MASK (1 << 8) +#define CODEC_BIT8_STR "Dolby Digital Plus\n" +#define CODEC_BIT11_MASK (1 << 11) +#define CODEC_BIT11_STR "ALAC\n" +#define CODEC_BIT12_MASK (1 << 12) +#define CODEC_BIT12_STR "MS11\n" +#define CODEC_BIT13_MASK (1 << 13) +#define CODEC_BIT13_STR "SBC\n" + + if (bits & CODEC_BIT0_MASK) { + strcpy(codecs, CODEC_BIT0_STR); + codecs += sizeof(CODEC_BIT0_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT1_MASK) { + strcpy(codecs, CODEC_BIT1_STR); + codecs += sizeof(CODEC_BIT1_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT2_MASK) { + strcpy(codecs, CODEC_BIT2_STR); + codecs += sizeof(CODEC_BIT2_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT3_MASK) { + strcpy(codecs, CODEC_BIT3_STR); + codecs += sizeof(CODEC_BIT3_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT4_MASK) { + strcpy(codecs, CODEC_BIT4_STR); + codecs += sizeof(CODEC_BIT4_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT5_MASK) { + strcpy(codecs, CODEC_BIT5_STR); + codecs += sizeof(CODEC_BIT5_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT6_MASK) { + strcpy(codecs, CODEC_BIT6_STR); + codecs += sizeof(CODEC_BIT6_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT7_MASK) { + strcpy(codecs, CODEC_BIT7_STR); + codecs += sizeof(CODEC_BIT7_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT8_MASK) { + strcpy(codecs, CODEC_BIT8_STR); + codecs += sizeof(CODEC_BIT8_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT11_MASK) { + strcpy(codecs, CODEC_BIT11_STR); + codecs += sizeof(CODEC_BIT11_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT12_MASK) { + strcpy(codecs, CODEC_BIT12_STR); + codecs += sizeof(CODEC_BIT12_STR); + codecs--; /* terminating null */ + } + if (bits & CODEC_BIT13_MASK) { + strcpy(codecs, CODEC_BIT13_STR); + codecs += sizeof(CODEC_BIT13_STR); + codecs--; /* terminating null */ + } + *codecs = '\0'; +} + +/* Fills @codec with the codec corresponding to @codec_num */ +static void parse_codec_by_number(unsigned int codec_num, char *codecs) +{ + parse_codec_by_bit(1 << codec_num, codecs); +} + +/* Fills @upmix by the current upmix setting */ +static void parse_upmix(unsigned int setting, char *upmix) +{ +#define UPMIX_0_STR "Pass through\n" +#define UPMIX_1_STR "Simple 5.1\n" +#define UPMIX_2_STR "Dolby Pro Logic 2\n" + + switch (setting) { + case 0: + strcpy(upmix, UPMIX_0_STR); + upmix += sizeof(UPMIX_0_STR); + upmix--; /* terminating null */ + break; + case 1: + strcpy(upmix, UPMIX_1_STR); + upmix += sizeof(UPMIX_1_STR); + upmix--; /* terminating null */ + break; + case 2: + strcpy(upmix, UPMIX_2_STR); + upmix += sizeof(UPMIX_2_STR); + upmix--; /* terminating null */ + break; + default: + break; + } + *upmix = '\0'; +} + +/* Fills @downmix by the current downmix setting */ +static void parse_downmix(unsigned int setting, char *downmix) +{ +#define DOWNMIX_0_STR "Pass through\n" +#define DOWNMIX_1_STR "5.1\n" +#define DOWNMIX_2_STR "2.0\n" + + switch (setting) { + case 0: + strcpy(downmix, DOWNMIX_0_STR); + downmix += sizeof(DOWNMIX_0_STR); + downmix--; /* terminating null */ + break; + case 1: + strcpy(downmix, DOWNMIX_1_STR); + downmix += sizeof(DOWNMIX_1_STR); + downmix--; /* terminating null */ + break; + case 2: + strcpy(downmix, DOWNMIX_2_STR); + downmix += sizeof(DOWNMIX_2_STR); + downmix--; /* terminating null */ + break; + default: + break; + } + *downmix = '\0'; +} + +/* Fills @mux by the current setting of mixer's output @pipe mux */ +static void parse_mux(unsigned int setting, char *mux) +{ +#define MUX_0_STR "mix\n" +#define MUX_1_STR "input 0\n" +#define MUX_2_STR "input 1\n" +#define MUX_3_STR "input 2\n" +#define MUX_4_STR "input 3\n" + + switch (setting) { + case 0: + strcpy(mux, MUX_0_STR); + mux += sizeof(MUX_0_STR); + mux--; /* terminating null */ + break; + case 1: + strcpy(mux, MUX_1_STR); + mux += sizeof(MUX_1_STR); + mux--; /* terminating null */ + break; + case 2: + strcpy(mux, MUX_2_STR); + mux += sizeof(MUX_2_STR); + mux--; /* terminating null */ + break; + case 3: + strcpy(mux, MUX_3_STR); + mux += sizeof(MUX_3_STR); + mux--; /* terminating null */ + break; + case 4: + strcpy(mux, MUX_4_STR); + mux += sizeof(MUX_4_STR); + mux--; /* terminating null */ + break; + default: + break; + } + *mux = '\0'; +} + +/* Info API */ +/* Sets the major and minor numbers of the currently running AXD firmware */ +void axd_cmd_get_version(struct axd_cmd *cmd, + int *major, int *minor, int *patch) +{ + unsigned int version; + + axd_read_reg(cmd, AXD_REG_VERSION, &version); + if (unlikely(!major || !minor)) + return; + *major = (version >> 22); /* top 10 bits */ + *minor = (version >> 12) & 0x3FF; /* middle 10 bits */ + *patch = version & 0xFFF; /* bottom 12 bits */ +} + +/* Sets the number of supported in/out pipes */ +int axd_cmd_get_num_pipes(struct axd_cmd *cmd, + unsigned int *inpipes, unsigned int *outpipes) +{ + unsigned int config0; + int ret; + + ret = axd_read_reg(cmd, AXD_REG_CONFIG0, &config0); + if (unlikely(!inpipes || !outpipes)) + return -1; + if (ret) + return -1; + *inpipes = config0 >> 16; + *inpipes &= 0xFF; + *outpipes = config0 & 0xFF; + return 0; +} + +/* Fills @codecs with a list of supported codecs */ +void axd_cmd_get_decoders(struct axd_cmd *cmd, char *codecs) +{ + unsigned int config1; + + axd_read_reg(cmd, AXD_REG_CONFIG1, &config1); + if (unlikely(!codecs)) + return; + parse_codec_by_bit(config1, codecs); +} + +/* Fills @codecs with a list of supported codecs */ +void axd_cmd_get_encoders(struct axd_cmd *cmd, char *codecs) +{ + unsigned int config2; + + axd_read_reg(cmd, AXD_REG_CONFIG2, &config2); + if (unlikely(!codecs)) + return; + parse_codec_by_bit(config2, codecs); +} + +/* Returns non-zero if Mix/Xbar is present. Zero otherwise. */ +int axd_cmd_xbar_present(struct axd_cmd *cmd) +{ + unsigned int temp; + + axd_read_reg(cmd, AXD_REG_CONFIG3, &temp); + return temp & 0x1; +} + +/* Returns non-zero if mixer EQ is enabled. Zero otherwise. */ +int axd_cmd_mixer_get_eqenabled(struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int control; + + axd_read_reg(cmd, AXD_REG_EQ_CTRL_GAIN, &control); + return (control & AXD_EQCTRL_ENABLE_BITS) >> AXD_EQCTRL_ENABLE_SHIFT; +} + +/* Sets @gain to the currently set output EQ Master gain value */ +void axd_cmd_mixer_get_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int control; + + axd_read_reg(cmd, AXD_REG_EQ_CTRL_GAIN, &control); + *gain = (control & AXD_EQCTRL_GAIN_BITS) >> AXD_EQCTRL_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band0 gain value */ +void axd_cmd_mixer_get_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int temp; + + axd_read_reg(cmd, AXD_REG_EQ_BAND0, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band1 gain value */ +void axd_cmd_mixer_get_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int temp; + + axd_read_reg(cmd, AXD_REG_EQ_BAND1, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band2 gain value */ +void axd_cmd_mixer_get_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int temp; + + axd_read_reg(cmd, AXD_REG_EQ_BAND2, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band3 gain value */ +void axd_cmd_mixer_get_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int temp; + + axd_read_reg(cmd, AXD_REG_EQ_BAND3, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band4 gain value */ +void axd_cmd_mixer_get_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int temp; + + axd_read_reg(cmd, AXD_REG_EQ_BAND4, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @mux to the currently selected mux output @pipe of mixer */ +void axd_cmd_mixer_get_mux(struct axd_cmd *cmd, unsigned int pipe, + char *mux) +{ + unsigned int reg = axd_get_mixer_mux_reg(cmd, pipe); + unsigned int setting; + + if (unlikely(!reg || !mux)) + return; + axd_read_reg(cmd, reg, &setting); + parse_mux(setting, mux); +} + +/* Returns non-zero of input @pipe is enabled. Zero otherwise. */ +int axd_cmd_input_get_enabled(struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg = axd_get_input_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_INCTRL_ENABLE_BITS) >> AXD_INCTRL_ENABLE_SHIFT; +} + +/* Sets @source to the currently selected source of input @pipe */ +void axd_cmd_input_get_source(struct axd_cmd *cmd, unsigned int pipe, + char *source) +{ + unsigned int reg = axd_get_input_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || !source)) + return; + axd_read_reg(cmd, reg, &control); + control = (control & AXD_INCTRL_SOURCE_BITS) >> AXD_INCTRL_SOURCE_SHIFT; + parse_source(control, source); +} + +/* Sets @codec to the currently selected codec of input @pipe */ +void axd_cmd_input_get_codec(struct axd_cmd *cmd, unsigned int pipe, + char *codec) +{ + unsigned int codec_num = axd_get_input_codec_number(cmd, pipe); + + if (unlikely(!codec)) + return; + parse_codec_by_number(codec_num, codec); +} + +/* Sets @gain to the currently set input gain value */ +void axd_cmd_input_get_gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_input_gain_reg(cmd, pipe); + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, gain); +} + +/* Sets @gain to the currently set input gain value */ +void axd_cmd_input_get_mute(struct axd_cmd *cmd, unsigned int pipe, + int *muted) +{ + unsigned int reg = axd_get_input_gain_reg(cmd, pipe); + + if (unlikely(!reg || !muted)) + return; + axd_read_reg(cmd, reg, muted); +} + +/* Sets @upmix to the currently selected upmix setting of input @pipe */ +void axd_cmd_input_get_upmix(struct axd_cmd *cmd, unsigned int pipe, + char *upmix) +{ + unsigned int reg = axd_get_input_upmix_reg(cmd, pipe); + unsigned int setting; + + if (unlikely(!reg || !upmix)) + return; + axd_read_reg(cmd, reg, &setting); + parse_upmix(setting, upmix); +} + +/* Returns the buffer occupancy value of @pipe. */ +unsigned int axd_cmd_input_get_buffer_occupancy(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int bo; + unsigned int reg = axd_get_input_buffer_occupancy_reg(cmd, pipe); + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &bo); + return bo; +} + +/* Returns non-zero of output @pipe is enabled. Zero otherwise. */ +int axd_cmd_output_get_enabled(struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg = axd_get_output_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_OUTCTRL_ENABLE_BITS) >> AXD_OUTCTRL_ENABLE_SHIFT; +} + +/* Sets @codec to the currently selected codec of output @pipe */ +void axd_cmd_output_get_codec(struct axd_cmd *cmd, unsigned int pipe, + char *codec) +{ + unsigned int codec_num = axd_get_output_codec_number(cmd, pipe); + + if (unlikely(!codec)) + return; + parse_codec_by_number(codec_num, codec); +} + +/* Sets @sink to the currently selected sink of output @pipe */ +void axd_cmd_output_get_sink(struct axd_cmd *cmd, unsigned int pipe, + char *sink) +{ + unsigned int reg = axd_get_output_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg || !sink)) + return; + axd_read_reg(cmd, reg, &control); + control = (control & AXD_OUTCTRL_SINK_BITS) >> AXD_OUTCTRL_SINK_SHIFT; + parse_sink(control, sink); +} + +/* Sets @downmix to the currently selected downmix setting of output @pipe */ +void axd_cmd_output_get_downmix(struct axd_cmd *cmd, unsigned int pipe, + char *downmix) +{ + unsigned int reg = axd_get_output_downmix_reg(cmd, pipe); + unsigned int setting; + + if (unlikely(!reg || !downmix)) + return; + axd_read_reg(cmd, reg, &setting); + parse_downmix(setting, downmix); +} + +/* Returns non-zero of output @pipe EQ is enabled. Zero otherwise. */ +int axd_cmd_output_get_eqenabled(struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg = axd_get_output_eqcontrol_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_EQCTRL_ENABLE_BITS) >> AXD_EQCTRL_ENABLE_SHIFT; +} + +/* Sets @gain to the currently set output EQ Master gain value */ +void axd_cmd_output_get_eqmastergain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_output_eqcontrol_reg(cmd, pipe); + unsigned int temp; + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, &temp); + *gain = ((int)temp & AXD_EQCTRL_GAIN_BITS) >> AXD_EQCTRL_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band0 gain value */ +void axd_cmd_output_get_eqband0gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_output_eqband0_reg(cmd, pipe); + unsigned int temp; + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band1 gain value */ +void axd_cmd_output_get_eqband1gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_output_eqband1_reg(cmd, pipe); + unsigned int temp; + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band2 gain value */ +void axd_cmd_output_get_eqband2gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_output_eqband2_reg(cmd, pipe); + unsigned int temp; + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band3 gain value */ +void axd_cmd_output_get_eqband3gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_output_eqband3_reg(cmd, pipe); + unsigned int temp; + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +/* Sets @gain to the currently set output EQ Band4 gain value */ +void axd_cmd_output_get_eqband4gain(struct axd_cmd *cmd, unsigned int pipe, + int *gain) +{ + unsigned int reg = axd_get_output_eqband4_reg(cmd, pipe); + unsigned int temp; + + if (unlikely(!reg || !gain)) + return; + axd_read_reg(cmd, reg, &temp); + *gain = ((int)temp & AXD_EQBANDX_GAIN_BITS) >> AXD_EQBANDX_GAIN_SHIFT; +} + +void axd_cmd_output_get_geq_power(struct axd_cmd *cmd, unsigned int pipe, + char *buf, int channel) +{ + u32 data[5]; + int i; + + if (channel < 4) { + for (i = 0; i < 5; i++) { + u32 reg = axd_get_output_eq_power_reg_ch0_3(cmd, + pipe, i); + + if (unlikely(!reg)) + return; + + if (axd_read_reg(cmd, reg, &data[i])) + return; + } + + sprintf(buf, "%d, %d, %d, %d, %d\n", + (data[0] >> (channel * 8)) & 0xFF, + (data[1] >> (channel * 8)) & 0xFF, + (data[2] >> (channel * 8)) & 0xFF, + (data[3] >> (channel * 8)) & 0xFF, + (data[3] >> (channel * 8)) & 0xFF); + + } else { + for (i = 0; i < 5; i++) { + u32 reg = axd_get_output_eq_power_reg_ch4_7(cmd, + pipe, i); + + if (unlikely(!reg)) + return; + + if (axd_read_reg(cmd, reg, &data[i])) + return; + } + + sprintf(buf, "%d, %d, %d, %d, %d\n", + (data[0] >> (channel-4 * 8)) & 0xFF, + (data[1] >> (channel-4 * 8)) & 0xFF, + (data[2] >> (channel-4 * 8)) & 0xFF, + (data[3] >> (channel-4 * 8)) & 0xFF, + (data[4] >> (channel-4 * 8)) & 0xFF); + } +} + +unsigned int axd_cmd_info_get_resampler_fin(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int temp; + unsigned int reg = axd_get_resample_fin_reg(cmd, pipe); + + axd_read_reg(cmd, reg, &temp); + + return temp; +} + +unsigned int axd_cmd_info_get_resampler_fout(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int temp; + unsigned int reg = axd_get_resample_fout_reg(cmd, pipe); + + axd_read_reg(cmd, reg, &temp); + + return temp; +} + +void axd_cmd_info_set_resampler_fout(struct axd_cmd *cmd, + unsigned int pipe, unsigned int fout) +{ + unsigned int reg = axd_get_resample_fout_reg(cmd, pipe); + + axd_write_reg(cmd, reg, fout); +} + +unsigned int axd_cmd_output_get_dcpp_enabled(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_ENABLE_BITS) >> + AXD_DCPP_CTRL_ENABLE_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_mode(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_MODE_BITS) >> AXD_DCPP_CTRL_MODE_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_channels(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_CHANNELS_BITS) >> + AXD_DCPP_CTRL_CHANNELS_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_eq_mode(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_EQ_MODE_BITS) >> + AXD_DCPP_CTRL_EQ_MODE_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_eq_bands(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_EQ_BANDS_BITS) >> + AXD_DCPP_CTRL_EQ_BANDS_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_max_delay_samples(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_max_delay_samples_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_delay_samples(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_delay_samples_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_output_volume( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_eq_output_volume_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_eq_passthrough_gain_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_eq_inverse_passthrough_gain_reg(cmd, + pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_shift( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_shift_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_a0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_bass_shelf_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_bass_shelf_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_shift( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_shift_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_a0( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_a0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_a1( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_a2( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_b0( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_treble_shelf_b1( + struct axd_cmd *cmd, unsigned int pipe, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + + reg = axd_get_output_dcpp_channel_treble_shelf_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_gain_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_channel_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int channel, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, false, channel); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_shift_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_bands(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_BITS) + >> AXD_DCPP_CTRL_SUBBAND_EQ_BANDS_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_subband_enabled(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_SUBBAND_ENABLE_BITS) + >> AXD_DCPP_CTRL_SUBBAND_ENABLE_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_subband_input_channel_mask( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg = axd_get_output_dcpp_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return (control & AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_BITS) + >> AXD_DCPP_CTRL_SUBBAND_CHANNEL_MASK_SHIFT; +} + +unsigned int axd_cmd_output_get_dcpp_subband_delay_samples(struct axd_cmd *cmd, + unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_delay_samples_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_output_volume( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_eq_output_volume_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_eq_passthrough_gain_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_inverse_passthrough_gain( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + + reg = axd_get_output_dcpp_channel_eq_inverse_passthrough_gain_reg(cmd, + pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_gain(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_gain_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_a0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_a1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int control; + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_a2(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_b0(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int control; + unsigned int reg; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_b1(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_eq_shift(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + unsigned int reg; + unsigned int control; + + axd_cmd_output_dcpp_select_channel(cmd, pipe, true, 0); + axd_cmd_output_dcpp_select_band(cmd, pipe, band); + + reg = axd_get_output_dcpp_channel_eq_shift_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_a0( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_subband_low_pass_filter_a0_reg(cmd, pipe); + + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_a1( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_subband_low_pass_filter_a1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_a2( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_subband_low_pass_filter_a2_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_b0( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_subband_low_pass_filter_b0_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} + +unsigned int axd_cmd_output_get_dcpp_subband_low_pass_filter_b1( + struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg; + unsigned int control; + + reg = axd_get_output_dcpp_subband_low_pass_filter_b1_reg(cmd, pipe); + if (unlikely(!reg)) + return 0; + axd_read_reg(cmd, reg, &control); + return control; +} diff --git a/drivers/char/axd/axd_cmds_internal.c b/drivers/char/axd/axd_cmds_internal.c new file mode 100644 index 000000000000..1e23d1329193 --- /dev/null +++ b/drivers/char/axd/axd_cmds_internal.c @@ -0,0 +1,3237 @@ +/* + * 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. + * + * Common functionality required by other axd_cmds_*.c files. + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/sched.h> + +#include "axd_cmds_internal.h" +#include "axd_module.h" +#include "axd_platform.h" + +#define WRONG_PIPE_STR "Wrong pipe number: %d\n" +#define WRONG_BAND_STR "Wrong band number: %d\n" + +/* + * Send/Clear control kick. + * + * NOTE: + * Must acquire axd_platform_lock() before accessing kick and interrupt status + * registers as the AXD firmware might be accessing them at the same time. + */ +inline void axd_ctrl_kick(struct axd_memory_map __iomem *message) +{ + unsigned long flags; + unsigned int temp; + + flags = axd_platform_lock(); + temp = ioread32(&message->kick) | AXD_ANY_KICK_BIT | AXD_KICK_CTRL_BIT; + iowrite32(temp, &message->kick); + axd_platform_unlock(flags); + axd_platform_kick(); +} +inline void axd_kick_status_clear(struct axd_memory_map __iomem *message) +{ + unsigned long flags; + unsigned int temp; + + flags = axd_platform_lock(); + temp = ioread32(&message->int_status) & ~AXD_INT_KICK_DONE; + iowrite32(temp, &message->int_status); + axd_platform_unlock(flags); +} +/* + * Wait until axd is ready again. Must be called while cm_lock is held. + */ +int axd_wait_ready(struct axd_memory_map __iomem *message) +{ +#define BUSYWAIT_TIME 1 +#define BUSYWAIT_TIMEOUT 100 + unsigned int timeout = 0; + + while (ioread32(&message->control_command) != AXD_CTRL_CMD_READY) { + mdelay(BUSYWAIT_TIME); + timeout += BUSYWAIT_TIME; + if (timeout == BUSYWAIT_TIMEOUT) + return -1; + } + return 0; +} + +/* + * Reads a register from the MemoryMapped register interface. + * @cmd: pointer to initialized struct axd_cmd. + * @reg: the register address to be accessed. + */ +int axd_read_reg(struct axd_cmd *cmd, unsigned int reg, unsigned int *data) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct axd_memory_map __iomem *message = cmd->message; + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + axd_set_flag(&cmd->response_flg, 0); + iowrite32(AXD_CTRL_CMD_READ_REGISTER | reg, &message->control_command); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + *data = ioread32(&message->control_data); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->dev, "failed to read reg 0x%04X\n", reg); + *data = 0; + return -1; + } + return 0; +} + +/* + * Writes control data to the MemoryMapped control interface. + * We assume that cm_lock is held before this function is called. + * @cmd: pointer to initialized struct axd_cmd. + * @ctrl_command: the control command to write. + * @ctrl_data: the control value to write. + */ +int axd_write_ctrl(struct axd_cmd *cmd, unsigned int ctrl_command, + unsigned int ctrl_data) +{ + struct axd_memory_map __iomem *message = cmd->message; + int ret; + + axd_set_flag(&cmd->response_flg, 0); + iowrite32(ctrl_data, &message->control_data); + iowrite32(ctrl_command, &message->control_command); + axd_ctrl_kick(message); + ret = wait_event_timeout(cmd->wait, + axd_get_flag(&cmd->response_flg) != 0, CMD_TIMEOUT); + return ret; +} + +/* + * Writes value to a register int the MemoryMapped register interface. + * @cmd: pointer to initialized struct axd_cmd. + * @reg: the register address to be accessed. + * @value: the new value to write. + */ +int axd_write_reg(struct axd_cmd *cmd, unsigned int reg, unsigned int value) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + ret = axd_write_ctrl(cmd, AXD_CTRL_CMD_WRITE_REGISTER | reg, value); + mutex_unlock(cm_lock); + if (!ret) { + dev_warn(axd->dev, "failed to write reg 0x%04X\n", reg); + return -1; + } + + return 0; +} + +int axd_write_reg_buf(struct axd_cmd *cmd, unsigned int reg, unsigned int value) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct mutex *cm_lock = &cmd->cm_lock; + struct axd_ctrlbuf_item __iomem *buf; + unsigned int ctrlbuf_ctrl = ioread32(&cmd->message->ctrlbuf_ctrl); + unsigned int ctrlbuf_size = ioread32(&cmd->message->ctrlbuf_size); + unsigned int temp; + + if (!axd_get_flag(&cmd->ctrlbuf_active_flg)) { + /* If the ctrlbuf isn't active, fall back to simple reg write */ + return axd_write_reg(cmd, reg, value); + } + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + + if (ctrlbuf_ctrl >= ctrlbuf_size) { + mutex_unlock(cm_lock); + dev_err(axd->dev, "Could not write to ctrlbuf: full\n"); + return -1; + } + + buf = &cmd->message->ctrlbuf[ctrlbuf_ctrl]; + + iowrite32(AXD_CTRL_CMD_WRITE_REGISTER | reg, &buf->reg); + iowrite32(value, &buf->val); + + temp = ioread32(&cmd->message->ctrlbuf_ctrl) + 1; + iowrite32(temp, &cmd->message->ctrlbuf_ctrl); + + mutex_unlock(cm_lock); + + return 0; +} + +int axd_flush_reg_buf(struct axd_cmd *cmd) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + struct mutex *cm_lock = &cmd->cm_lock; + int ret; + + mutex_lock(cm_lock); + if (axd_get_flag(&cmd->fw_stopped_flg)) { + mutex_unlock(cm_lock); + return -1; + } + + if (ioread32(&cmd->message->ctrlbuf_ctrl) == 0) { + mutex_unlock(cm_lock); + dev_warn(axd->dev, "Tried to flush empty ctrlbuf\n"); + return -1; + } + + ret = axd_write_ctrl(cmd, AXD_CTRL_CMD_CTRLBUF_FLUSH, 0); + if (!ret) { + /* Drop buffer and ignore any response */ + iowrite32(0, &cmd->message->ctrlbuf_ctrl); + + mutex_unlock(cm_lock); + dev_err(axd->dev, "Could not write control command to flush buffer"); + return -EIO; + } + + /* Ignore any response */ + iowrite32(0, &cmd->message->ctrlbuf_ctrl); + + mutex_unlock(cm_lock); + + return 0; +} + +/* Returns the address of the correct mixer mux register for @pipe */ +unsigned int axd_get_mixer_mux_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_MUX0; + break; + case 1: + reg = AXD_REG_MUX1; + break; + case 2: + reg = AXD_REG_MUX2; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the number of the currently set input codec */ +unsigned int axd_get_input_codec_number(struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg = axd_get_input_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return -1; + axd_read_reg(cmd, reg, &control); + return (control & AXD_INCTRL_CODEC_BITS) >> AXD_INCTRL_CODEC_SHIFT; +} + +/* Returns the address of the correct input control register for @pipe */ +unsigned int axd_get_input_control_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_INPUT0_CONTROL; + break; + case 1: + reg = AXD_REG_INPUT1_CONTROL; + break; + case 2: + reg = AXD_REG_INPUT2_CONTROL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the correct input gain register for @pipe */ +unsigned int axd_get_input_gain_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_INPUT0_GAIN; + break; + case 1: + reg = AXD_REG_INPUT1_GAIN; + break; + case 2: + reg = AXD_REG_INPUT2_GAIN; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the correct input mute register for @pipe */ +unsigned int axd_get_input_mute_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_INPUT0_MUTE; + break; + case 1: + reg = AXD_REG_INPUT1_MUTE; + break; + case 2: + reg = AXD_REG_INPUT2_MUTE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the correct input UpMix register for @pipe */ +unsigned int axd_get_input_upmix_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_INPUT0_UPMIX; + break; + case 1: + reg = AXD_REG_INPUT1_UPMIX; + break; + case 2: + reg = AXD_REG_INPUT2_UPMIX; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the correct input bufer occupancy reg for @pipe */ +unsigned int axd_get_input_buffer_occupancy_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_INPUT0_BUFFER_OCCUPANCY; + break; + case 1: + reg = AXD_REG_INPUT1_BUFFER_OCCUPANCY; + break; + case 2: + reg = AXD_REG_INPUT2_BUFFER_OCCUPANCY; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the number of the currently set output codec */ +unsigned int axd_get_output_codec_number(struct axd_cmd *cmd, unsigned int pipe) +{ + unsigned int reg = axd_get_output_control_reg(cmd, pipe); + unsigned int control; + + if (unlikely(!reg)) + return -1; + axd_read_reg(cmd, reg, &control); + return (control & AXD_OUTCTRL_CODEC_BITS) >> AXD_OUTCTRL_CODEC_SHIFT; +} + +/* Returns the address of the correct output control register for @pipe */ +unsigned int axd_get_output_control_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_CONTROL; + break; + case 1: + reg = AXD_REG_OUTPUT1_CONTROL; + break; + case 2: + reg = AXD_REG_OUTPUT2_CONTROL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the correct output DownMix register for @pipe */ +unsigned int axd_get_output_downmix_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DOWNMIX; + break; + case 1: + reg = AXD_REG_OUTPUT1_DOWNMIX; + break; + case 2: + reg = AXD_REG_OUTPUT2_DOWNMIX; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* + * Returns the address of the output EQ Ctrl / Master Gain register for + * @pipe + */ +unsigned int axd_get_output_eqcontrol_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_EQCTRL; + break; + case 1: + reg = AXD_REG_OUTPUT1_EQCTRL; + break; + case 2: + reg = AXD_REG_OUTPUT2_EQCTRL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the output EQ Band0 register for @pipe*/ +unsigned int axd_get_output_eqband0_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_EQBAND0; + break; + case 1: + reg = AXD_REG_OUTPUT1_EQBAND0; + break; + case 2: + reg = AXD_REG_OUTPUT2_EQBAND0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the output EQ Band1 register for @pipe*/ +unsigned int axd_get_output_eqband1_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_EQBAND1; + break; + case 1: + reg = AXD_REG_OUTPUT1_EQBAND1; + break; + case 2: + reg = AXD_REG_OUTPUT2_EQBAND1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the output EQ Band2 register for @pipe*/ +unsigned int axd_get_output_eqband2_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_EQBAND2; + break; + case 1: + reg = AXD_REG_OUTPUT1_EQBAND2; + break; + case 2: + reg = AXD_REG_OUTPUT2_EQBAND2; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the output EQ Band3 register for @pipe*/ +unsigned int axd_get_output_eqband3_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_EQBAND3; + break; + case 1: + reg = AXD_REG_OUTPUT1_EQBAND3; + break; + case 2: + reg = AXD_REG_OUTPUT2_EQBAND3; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the output EQ Band4 register for @pipe*/ +unsigned int axd_get_output_eqband4_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_EQBAND4; + break; + case 1: + reg = AXD_REG_OUTPUT1_EQBAND4; + break; + case 2: + reg = AXD_REG_OUTPUT2_EQBAND4; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* DCPP */ + +int axd_cmd_output_dcpp_select_channel(struct axd_cmd *cmd, unsigned int pipe, + bool subband, unsigned int channel) +{ + unsigned int reg; + unsigned int control; + int ret; + + reg = axd_get_output_dcpp_channel_ctrl_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + /* Generate channel selector */ + control = 0; + + if (subband) + control = AXD_DCPP_CHANNEL_CTRL_SUBBAND_BITS; + else + control = channel << AXD_DCPP_CHANNEL_CTRL_CHANNEL_SHIFT; + + /* Compare with last channel selector */ + if (control == cmd->dcpp_channel_ctrl_cache[pipe]) { + ret = 0; + } else { + ret = axd_write_reg_buf(cmd, reg, control); + cmd->dcpp_channel_ctrl_cache[pipe] = control; + } + + return ret; +} + +int axd_cmd_output_dcpp_select_band(struct axd_cmd *cmd, unsigned int pipe, + unsigned int band) +{ + unsigned int reg; + unsigned int control; + int ret; + + reg = axd_get_output_dcpp_band_ctrl_reg(cmd, pipe); + if (unlikely(!reg)) + return -1; + + /* Generate band selector */ + control = band; + + /* Compare with last band selector */ + if (control == cmd->dcpp_band_ctrl_cache[pipe]) { + ret = 0; + } else { + ret = axd_write_reg_buf(cmd, reg, control); + cmd->dcpp_band_ctrl_cache[pipe] = control; + } + + return ret; +} + +unsigned int axd_get_output_dcpp_control_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CONTROL; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CONTROL; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CONTROL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_max_delay_samples_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_MAX_DELAY_SAMPLES; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_MAX_DELAY_SAMPLES; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_MAX_DELAY_SAMPLES; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_ctrl_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_CONTROL; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_CONTROL; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_CONTROL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + } + return reg; +} + +unsigned int axd_get_output_dcpp_band_ctrl_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_BAND_CONTROL; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_BAND_CONTROL; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_BAND_CONTROL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_delay_samples_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_DELAY_SAMPLES; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_DELAY_SAMPLES; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_DELAY_SAMPLES; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_output_volume_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_OUTPUT_VOLUME; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_OUTPUT_VOLUME; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_OUTPUT_VOLUME; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_passthrough_gain_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_PASSTHROUGH_GAIN; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_PASSTHROUGH_GAIN; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_PASSTHROUGH_GAIN; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_inverse_passthrough_gain_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_INVERSE_PASSTHROUGH_GAIN; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_INVERSE_PASSTHROUGH_GAIN; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_INVERSE_PASSTHROUGH_GAIN; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_bass_shelf_shift_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_SHIFT; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_SHIFT; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_SHIFT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_bass_shelf_a0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_A0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_A0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_A0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_bass_shelf_a1_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_A1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_A1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_A1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_bass_shelf_a2_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_A2; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_A2; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_A2; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_bass_shelf_b0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_B0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_B0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_B0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_bass_shelf_b1_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_BASS_SHELF_B1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_BASS_SHELF_B1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_BASS_SHELF_B1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_treble_shelf_shift_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_SHIFT; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_SHIFT; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_SHIFT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_treble_shelf_a0_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_A0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_A0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_A0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_treble_shelf_a1_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_A1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_A1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_A1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_treble_shelf_a2_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_A2; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_A2; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_A2; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_treble_shelf_b0_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_B0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_B0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_B0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_treble_shelf_b1_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_TREBLE_SHELF_B1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_TREBLE_SHELF_B1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_TREBLE_SHELF_B1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_gain_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_GAIN; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_GAIN; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_GAIN; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_a0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_A0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_A0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_A0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_a1_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_A1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_A1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_A1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_a2_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_A2; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_A2; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_A2; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_b0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_B0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_B0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_B0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_b1_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_B1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_B1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_B1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_channel_eq_shift_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_CHANNEL_EQ_BAND_SHIFT; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_CHANNEL_EQ_BAND_SHIFT; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_CHANNEL_EQ_BAND_SHIFT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_subband_low_pass_filter_a0_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_A0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_A0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_A0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_subband_low_pass_filter_a1_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_A1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_A1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_A1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_subband_low_pass_filter_a2_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_A2; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_A2; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_A2; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_subband_low_pass_filter_b0_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_B0; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_B0; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_B0; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_dcpp_subband_low_pass_filter_b1_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + switch (pipe) { + case 0: + reg = AXD_REG_OUTPUT0_DCPP_SUBBAND_LOW_PASS_FILTER_B1; + break; + case 1: + reg = AXD_REG_OUTPUT1_DCPP_SUBBAND_LOW_PASS_FILTER_B1; + break; + case 2: + reg = AXD_REG_OUTPUT2_DCPP_SUBBAND_LOW_PASS_FILTER_B1; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the aac version for decoder at @pipe*/ +unsigned int axd_get_decoder_aac_version_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AAC_VERSION; + break; + case 1: + reg = AXD_REG_DEC1_AAC_VERSION; + break; + case 2: + reg = AXD_REG_DEC2_AAC_VERSION; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the aac pipes for decoder at @pipe*/ +unsigned int axd_get_decoder_aac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AAC_CHANNELS; + break; + case 1: + reg = AXD_REG_DEC1_AAC_CHANNELS; + break; + case 2: + reg = AXD_REG_DEC2_AAC_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the aac profile for decoder at @pipe*/ +unsigned int axd_get_decoder_aac_profile_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AAC_PROFILE; + break; + case 1: + reg = AXD_REG_DEC1_AAC_PROFILE; + break; + case 2: + reg = AXD_REG_DEC2_AAC_PROFILE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the aac stream type for decoder at @pipe*/ +unsigned int axd_get_decoder_aac_streamtype_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AAC_STREAM_TYPE; + break; + case 1: + reg = AXD_REG_DEC1_AAC_STREAM_TYPE; + break; + case 2: + reg = AXD_REG_DEC2_AAC_STREAM_TYPE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the aac stream type for decoder at @pipe*/ +unsigned int axd_get_decoder_aac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AAC_SAMPLERATE; + break; + case 1: + reg = AXD_REG_DEC1_AAC_SAMPLERATE; + break; + case 2: + reg = AXD_REG_DEC2_AAC_SAMPLERATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_decoder_ac3_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AC3_CHANNELS; + break; + case 1: + reg = AXD_REG_DEC1_AC3_CHANNELS; + break; + case 2: + reg = AXD_REG_DEC2_AC3_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_ac3_channel_order_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AC3_CHANNEL_ORDER; + break; + case 1: + reg = AXD_REG_DEC1_AC3_CHANNEL_ORDER; + break; + case 2: + reg = AXD_REG_DEC2_AC3_CHANNEL_ORDER; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_ac3_mode_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_AC3_MODE; + break; + case 1: + reg = AXD_REG_DEC1_AC3_MODE; + break; + case 2: + reg = AXD_REG_DEC2_AC3_MODE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the cook flavour for decoder at @pipe*/ +unsigned int axd_get_decoder_cook_flavour_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_COOK_FLAVOUR; + break; + case 1: + reg = AXD_REG_DEC1_COOK_FLAVOUR; + break; + case 2: + reg = AXD_REG_DEC2_COOK_FLAVOUR; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the flac pipes for decoder at @pipe*/ +unsigned int axd_get_decoder_flac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_FLAC_CHANNELS; + break; + case 1: + reg = AXD_REG_DEC1_FLAC_CHANNELS; + break; + case 2: + reg = AXD_REG_DEC2_FLAC_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the flac sample rate for decoder at @pipe*/ +unsigned int axd_get_decoder_flac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_FLAC_SAMPLERATE; + break; + case 1: + reg = AXD_REG_DEC1_FLAC_SAMPLERATE; + break; + case 2: + reg = AXD_REG_DEC2_FLAC_SAMPLERATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the flac bits per sample for decoder at @pipe*/ +unsigned int axd_get_decoder_flac_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_FLAC_BITS_PER_SAMPLE; + break; + case 1: + reg = AXD_REG_DEC1_FLAC_BITS_PER_SAMPLE; + break; + case 2: + reg = AXD_REG_DEC2_FLAC_BITS_PER_SAMPLE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the flac md5 checking for decoder at @pipe*/ +unsigned int axd_get_decoder_flac_md5checking_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_FLAC_MD5_CHECKING; + break; + case 1: + reg = AXD_REG_DEC1_FLAC_MD5_CHECKING; + break; + case 2: + reg = AXD_REG_DEC2_FLAC_MD5_CHECKING; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the mpeg num pipes for decoder at @pipe*/ +unsigned int axd_get_decoder_mpeg_numchannels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_MPEG_CHANNELS; + break; + case 1: + reg = AXD_REG_DEC1_MPEG_CHANNELS; + break; + case 2: + reg = AXD_REG_DEC2_MPEG_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the mpeg mlchannel for decoder at @pipe*/ +unsigned int axd_get_decoder_mpeg_mlchannel_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_MPEG_MLCHANNEL; + break; + case 1: + reg = AXD_REG_DEC1_MPEG_MLCHANNEL; + break; + case 2: + reg = AXD_REG_DEC2_MPEG_MLCHANNEL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma player opt for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_playeropt_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_PLAYER_OPT; + break; + case 1: + reg = AXD_REG_DEC1_WMA_PLAYER_OPT; + break; + case 2: + reg = AXD_REG_DEC2_WMA_PLAYER_OPT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma player drc setting for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_drcsetting_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_DRC_SETTING; + break; + case 1: + reg = AXD_REG_DEC1_WMA_DRC_SETTING; + break; + case 2: + reg = AXD_REG_DEC2_WMA_DRC_SETTING; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma player peak ref for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_peakampref_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_PEAK_AMP_REF; + break; + case 1: + reg = AXD_REG_DEC1_WMA_PEAK_AMP_REF; + break; + case 2: + reg = AXD_REG_DEC2_WMA_PEAK_AMP_REF; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma player rms ref for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_rmsampref_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_RMS_AMP_REF; + break; + case 1: + reg = AXD_REG_DEC1_WMA_RMS_AMP_REF; + break; + case 2: + reg = AXD_REG_DEC2_WMA_RMS_AMP_REF; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma player peak target for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_peakamptarget_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_PEAK_AMP_TARGET; + break; + case 1: + reg = AXD_REG_DEC1_WMA_PEAK_AMP_TARGET; + break; + case 2: + reg = AXD_REG_DEC2_WMA_PEAK_AMP_TARGET; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma player rms target for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_rmsamptarget_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_RMS_AMP_TARGET; + break; + case 1: + reg = AXD_REG_DEC2_WMA_RMS_AMP_TARGET; + break; + case 2: + reg = AXD_REG_DEC1_WMA_RMS_AMP_TARGET; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma pcm valid bits for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_pcmvalidbitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_PCM_VAL_BITS_PER_SAMPLE; + break; + case 1: + reg = AXD_REG_DEC1_WMA_PCM_VAL_BITS_PER_SAMPLE; + break; + case 2: + reg = AXD_REG_DEC2_WMA_PCM_VAL_BITS_PER_SAMPLE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma pcm container size for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_pcmcontainersize_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_PCM_CONTAINER_SIZE; + break; + case 1: + reg = AXD_REG_DEC1_WMA_PCM_CONTAINER_SIZE; + break; + case 2: + reg = AXD_REG_DEC2_WMA_PCM_CONTAINER_SIZE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format tag for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmaformattag_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_FORMAT_TAG; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_FORMAT_TAG; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_FORMAT_TAG; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format num pipes for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmanumchannels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_CHANNELS; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_CHANNELS; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format sample/s for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmasamplespersec_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_SAMPLES_PER_SEC; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_SAMPLES_PER_SEC; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_SAMPLES_PER_SEC; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* + * Returns the address of the wma format average bytes per sample for decoder + * at @pipe + */ +unsigned int axd_get_decoder_wma_wmaaveragebytespersec_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_AVG_BYTES_PER_SEC; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_AVG_BYTES_PER_SEC; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_AVG_BYTES_PER_SEC; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format block align for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmablockalign_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_BLOCK_ALIGN; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_BLOCK_ALIGN; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_BLOCK_ALIGN; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format valid bits for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmavalidbitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_VAL_BITS_PER_SAMPLE; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_VAL_BITS_PER_SAMPLE; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_VAL_BITS_PER_SAMPLE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format pipe mask for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmachannelmask_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_CHANNEL_MASK; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_CHANNEL_MASK; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_CHANNEL_MASK; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the wma format encode options for decoder at @pipe*/ +unsigned int axd_get_decoder_wma_wmaencodeoptions_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_WMA_WMA_ENCODE_OPTS; + break; + case 1: + reg = AXD_REG_DEC1_WMA_WMA_ENCODE_OPTS; + break; + case 2: + reg = AXD_REG_DEC2_WMA_WMA_ENCODE_OPTS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the pcm samplerate reg for decoder at @pipe*/ +unsigned int axd_get_decoder_pcm_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_PCMIN0_SAMPLE_RATE; + break; + case 1: + reg = AXD_REG_PCMIN1_SAMPLE_RATE; + break; + case 2: + reg = AXD_REG_PCMIN2_SAMPLE_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the pcm channels reg for decoder at @pipe*/ +unsigned int axd_get_decoder_pcm_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_PCMIN0_CHANNELS; + break; + case 1: + reg = AXD_REG_PCMIN1_CHANNELS; + break; + case 2: + reg = AXD_REG_PCMIN2_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the pcm bitspersample reg for decoder at @pipe*/ +unsigned int axd_get_decoder_pcm_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_PCMIN0_BITS_PER_SAMPLE; + break; + case 1: + reg = AXD_REG_PCMIN1_BITS_PER_SAMPLE; + break; + case 2: + reg = AXD_REG_PCMIN2_BITS_PER_SAMPLE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +/* Returns the address of the pcm justification reg for decoder at @pipe*/ +unsigned int axd_get_decoder_pcm_justification_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_PCMIN0_JUSTIFICATION; + break; + case 1: + reg = AXD_REG_PCMIN1_JUSTIFICATION; + break; + case 2: + reg = AXD_REG_PCMIN2_JUSTIFICATION; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_ddplus_config_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_DDPLUS_CONFIG; + break; + case 1: + reg = AXD_REG_DEC1_DDPLUS_CONFIG; + break; + case 2: + reg = AXD_REG_DEC2_DDPLUS_CONFIG; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_decoder_ddplus_channel_order_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_DDPLUS_CHANNEL_ORDER; + break; + case 1: + reg = AXD_REG_DEC1_DDPLUS_CHANNEL_ORDER; + break; + case 2: + reg = AXD_REG_DEC2_DDPLUS_CHANNEL_ORDER; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_alac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_ALAC_CHANNELS; + break; + case 1: + reg = AXD_REG_DEC1_ALAC_CHANNELS; + break; + case 2: + reg = AXD_REG_DEC2_ALAC_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_decoder_alac_depth_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_ALAC_DEPTH; + break; + case 1: + reg = AXD_REG_DEC1_ALAC_DEPTH; + break; + case 2: + reg = AXD_REG_DEC2_ALAC_DEPTH; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_decoder_alac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_ALAC_SAMPLE_RATE; + break; + case 1: + reg = AXD_REG_DEC1_ALAC_SAMPLE_RATE; + break; + case 2: + reg = AXD_REG_DEC2_ALAC_SAMPLE_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_decoder_alac_framelength_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_ALAC_FRAME_LENGTH; + break; + case 1: + reg = AXD_REG_DEC1_ALAC_FRAME_LENGTH; + break; + case 2: + reg = AXD_REG_DEC2_ALAC_FRAME_LENGTH; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_alac_maxframebytes_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_ALAC_MAX_FRAME_BYTES; + break; + case 1: + reg = AXD_REG_DEC1_ALAC_MAX_FRAME_BYTES; + break; + case 2: + reg = AXD_REG_DEC2_ALAC_MAX_FRAME_BYTES; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_decoder_alac_avgbitrate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_ALAC_AVG_BIT_RATE; + break; + case 1: + reg = AXD_REG_DEC1_ALAC_AVG_BIT_RATE; + break; + case 2: + reg = AXD_REG_DEC2_ALAC_AVG_BIT_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_sbc_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_SBC_SAMPLE_RATE; + break; + case 1: + reg = AXD_REG_DEC1_SBC_SAMPLE_RATE; + break; + case 2: + reg = AXD_REG_DEC2_SBC_SAMPLE_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_sbc_audiomode_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_SBC_AUDIO_MODE; + break; + case 1: + reg = AXD_REG_DEC1_SBC_AUDIO_MODE; + break; + case 2: + reg = AXD_REG_DEC2_SBC_AUDIO_MODE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_sbc_blocks_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_SBC_BLOCKS; + break; + case 1: + reg = AXD_REG_DEC1_SBC_BLOCKS; + break; + case 2: + reg = AXD_REG_DEC2_SBC_BLOCKS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_sbc_subbands_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_SBC_SUBBANDS; + break; + case 1: + reg = AXD_REG_DEC1_SBC_SUBBANDS; + break; + case 2: + reg = AXD_REG_DEC2_SBC_SUBBANDS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_sbc_bitpool_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_SBC_BITPOOL; + break; + case 1: + reg = AXD_REG_DEC1_SBC_BITPOOL; + break; + case 2: + reg = AXD_REG_DEC2_SBC_BITPOOL; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_decoder_sbc_allocationmode_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_DEC0_SBC_ALLOCATION_MODE; + break; + case 1: + reg = AXD_REG_DEC1_SBC_ALLOCATION_MODE; + break; + case 2: + reg = AXD_REG_DEC2_SBC_ALLOCATION_MODE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + + +unsigned int axd_get_decoder_ms11_mode_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + return AXD_REG_MS11_MODE; +} + +unsigned int axd_get_decoder_ms11_common_config0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + return AXD_REG_MS11_COMMON_CONFIG0; +} + +unsigned int axd_get_decoder_ms11_common_config1_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + return AXD_REG_MS11_COMMON_CONFIG1; +} + +unsigned int axd_get_decoder_ms11_ddt_config0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + return AXD_REG_MS11_DDT_CONFIG0; +} + +unsigned int axd_get_decoder_ms11_ddc_config0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + return AXD_REG_MS11_DDC_CONFIG0; +} + +unsigned int axd_get_decoder_ms11_ext_pcm_config0_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + return AXD_REG_MS11_EXT_PCM_CONFIG0; +} + +/* Returns the address of the pcm bitspersample reg for output at @pipe*/ +unsigned int axd_get_encoder_pcm_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_PCMOUT0_BITS_PER_SAMPLE; + break; + case 1: + reg = AXD_REG_PCMOUT1_BITS_PER_SAMPLE; + break; + case 2: + reg = AXD_REG_PCMOUT2_BITS_PER_SAMPLE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_encoder_flac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_CHANNELS; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_CHANNELS; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_BITS_PER_SAMPLE; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_BITS_PER_SAMPLE; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_BITS_PER_SAMPLE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_SAMPLE_RATE; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_SAMPLE_RATE; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_SAMPLE_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_totalsamples_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_TOTAL_SAMPLES; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_TOTAL_SAMPLES; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_TOTAL_SAMPLES; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_domidsidestereo_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_DO_MID_SIDE_STEREO; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_DO_MID_SIDE_STEREO; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_DO_MID_SIDE_STEREO; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_loosemidsidestereo_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_LOOSE_MID_SIDE_STEREO; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_LOOSE_MID_SIDE_STEREO; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_LOOSE_MID_SIDE_STEREO; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_doexhaustivemodelsearch_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_DO_EXHAUSTIVE_MODEL_SEARCH; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_DO_EXHAUSTIVE_MODEL_SEARCH; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_DO_EXHAUSTIVE_MODEL_SEARCH; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_minresidualpartitionorder_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_MIN_RESIDUAL_PARTITION_ORDER; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_MIN_RESIDUAL_PARTITION_ORDER; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_MIN_RESIDUAL_PARTITION_ORDER; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_maxresidualpartitionorder_reg( + struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_MAX_RESIDUAL_PARTITION_ORDER; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_MAX_RESIDUAL_PARTITION_ORDER; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_MAX_RESIDUAL_PARTITION_ORDER; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_blocksize_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_BLOCK_SIZE; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_BLOCK_SIZE; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_BLOCK_SIZE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_bytecount_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_BYTE_COUNT; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_BYTE_COUNT; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_BYTE_COUNT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_samplecount_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_SAMPLE_COUNT; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_SAMPLE_COUNT; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_SAMPLE_COUNT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_framecount_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_FRAME_COUNT; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_FRAME_COUNT; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_FRAME_COUNT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_flac_framebytes_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_FLAC_FRAME_BYTES; + break; + case 1: + reg = AXD_REG_ENC1_FLAC_FRAME_BYTES; + break; + case 2: + reg = AXD_REG_ENC2_FLAC_FRAME_BYTES; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_encoder_alac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_CHANNELS; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_CHANNELS; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_CHANNELS; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_alac_depth_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_DEPTH; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_DEPTH; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_DEPTH; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_alac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_SAMPLE_RATE; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_SAMPLE_RATE; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_SAMPLE_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_alac_framelength_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_FRAME_LENGTH; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_FRAME_LENGTH; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_FRAME_LENGTH; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_alac_maxframebytes_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_MAX_FRAME_BYTES; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_MAX_FRAME_BYTES; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_MAX_FRAME_BYTES; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_alac_avgbitrate_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_AVG_BIT_RATE; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_AVG_BIT_RATE; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_AVG_BIT_RATE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} +unsigned int axd_get_encoder_alac_fastmode_reg(struct axd_cmd *cmd, + unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_ENC0_ALAC_FAST_MODE; + break; + case 1: + reg = AXD_REG_ENC1_ALAC_FAST_MODE; + break; + case 2: + reg = AXD_REG_ENC2_ALAC_FAST_MODE; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_output_eq_power_reg_ch0_3(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + if (pipe == 0) { + switch (band) { + case 0: + reg = AXD_REG_EQ_OUT0_POWER_B0_C0_C3; + break; + case 1: + reg = AXD_REG_EQ_OUT0_POWER_B1_C0_C3; + break; + case 2: + reg = AXD_REG_EQ_OUT0_POWER_B2_C0_C3; + break; + case 3: + reg = AXD_REG_EQ_OUT0_POWER_B3_C0_C3; + break; + case 4: + reg = AXD_REG_EQ_OUT0_POWER_B4_C0_C3; + break; + default: + dev_err(axd->dev, WRONG_BAND_STR, band); + return 0; + } + } else if (pipe == 1) { + switch (band) { + case 0: + reg = AXD_REG_EQ_OUT1_POWER_B0_C0_C3; + break; + case 1: + reg = AXD_REG_EQ_OUT1_POWER_B1_C0_C3; + break; + case 2: + reg = AXD_REG_EQ_OUT1_POWER_B2_C0_C3; + break; + case 3: + reg = AXD_REG_EQ_OUT1_POWER_B3_C0_C3; + break; + case 4: + reg = AXD_REG_EQ_OUT1_POWER_B4_C0_C3; + break; + default: + dev_err(axd->dev, WRONG_BAND_STR, band); + return 0; + } + } else if (pipe == 2) { + switch (band) { + case 0: + reg = AXD_REG_EQ_OUT2_POWER_B0_C0_C3; + break; + case 1: + reg = AXD_REG_EQ_OUT2_POWER_B1_C0_C3; + break; + case 2: + reg = AXD_REG_EQ_OUT2_POWER_B2_C0_C3; + break; + case 3: + reg = AXD_REG_EQ_OUT2_POWER_B3_C0_C3; + break; + case 4: + reg = AXD_REG_EQ_OUT2_POWER_B4_C0_C3; + break; + default: + dev_err(axd->dev, WRONG_BAND_STR, band); + return 0; + } + } + return reg; +} + +unsigned int axd_get_output_eq_power_reg_ch4_7(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg = 0; + + if (pipe == 0) { + switch (band) { + case 0: + reg = AXD_REG_EQ_OUT0_POWER_B0_C4_C7; + break; + case 1: + reg = AXD_REG_EQ_OUT0_POWER_B1_C4_C7; + break; + case 2: + reg = AXD_REG_EQ_OUT0_POWER_B2_C4_C7; + break; + case 3: + reg = AXD_REG_EQ_OUT0_POWER_B3_C4_C7; + break; + case 4: + reg = AXD_REG_EQ_OUT0_POWER_B4_C4_C7; + break; + default: + dev_err(axd->dev, WRONG_BAND_STR, band); + return 0; + } + } else if (pipe == 1) { + switch (band) { + case 0: + reg = AXD_REG_EQ_OUT1_POWER_B0_C4_C7; + break; + case 1: + reg = AXD_REG_EQ_OUT1_POWER_B1_C4_C7; + break; + case 2: + reg = AXD_REG_EQ_OUT1_POWER_B2_C4_C7; + break; + case 3: + reg = AXD_REG_EQ_OUT1_POWER_B3_C4_C7; + break; + case 4: + reg = AXD_REG_EQ_OUT1_POWER_B4_C4_C7; + break; + default: + dev_err(axd->dev, WRONG_BAND_STR, band); + return 0; + } + } else if (pipe == 2) { + switch (band) { + case 0: + reg = AXD_REG_EQ_OUT2_POWER_B0_C4_C7; + break; + case 1: + reg = AXD_REG_EQ_OUT2_POWER_B1_C4_C7; + break; + case 2: + reg = AXD_REG_EQ_OUT2_POWER_B2_C4_C7; + break; + case 3: + reg = AXD_REG_EQ_OUT2_POWER_B3_C4_C7; + break; + case 4: + reg = AXD_REG_EQ_OUT2_POWER_B4_C4_C7; + break; + default: + dev_err(axd->dev, WRONG_BAND_STR, band); + return 0; + } + } + return reg; +} + +unsigned int axd_get_resample_fin_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_RESAMPLER0_FIN; + break; + case 1: + reg = AXD_REG_RESAMPLER1_FIN; + break; + case 2: + reg = AXD_REG_RESAMPLER2_FIN; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} + +unsigned int axd_get_resample_fout_reg(struct axd_cmd *cmd, unsigned int pipe) +{ + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + unsigned int reg; + + switch (pipe) { + case 0: + reg = AXD_REG_RESAMPLER0_FOUT; + break; + case 1: + reg = AXD_REG_RESAMPLER1_FOUT; + break; + case 2: + reg = AXD_REG_RESAMPLER2_FOUT; + break; + default: + dev_err(axd->dev, WRONG_PIPE_STR, pipe); + return 0; + } + return reg; +} diff --git a/drivers/char/axd/axd_cmds_internal.h b/drivers/char/axd/axd_cmds_internal.h new file mode 100644 index 000000000000..7fa90d0f44ab --- /dev/null +++ b/drivers/char/axd/axd_cmds_internal.h @@ -0,0 +1,306 @@ +/* + * 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. + * + * Common functionality required by other axd_cmds_*.c files. + */ +#ifndef AXD_CMDS_INTERNAL_H_ +#define AXD_CMDS_INTERNAL_H_ + +#include <linux/io.h> + +#include "axd_cmds.h" + +#ifdef DEBUG_BUFFERS +#define debugbuf printk +#else +#define debugbuf(format, ...) +#endif + +void axd_ctrl_kick(struct axd_memory_map __iomem *message); +void axd_kick_status_clear(struct axd_memory_map __iomem *message); +int axd_wait_ready(struct axd_memory_map __iomem *message); + +int axd_write_ctrl(struct axd_cmd *cmd, unsigned int ctrl_command, + unsigned int ctrl_data); + +int axd_read_reg(struct axd_cmd *cmd, unsigned int reg, unsigned int *data); +int axd_write_reg(struct axd_cmd *cmd, unsigned int reg, unsigned int value); + +int axd_write_reg_buf(struct axd_cmd *cmd, unsigned int reg, + unsigned int value); + +unsigned int axd_get_mixer_mux_reg(struct axd_cmd *cmd, unsigned int pipe); + +unsigned int axd_get_input_codec_number(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_input_control_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_input_gain_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_input_mute_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_input_upmix_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_input_buffer_occupancy_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_output_codec_number(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_control_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_downmix_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_eqcontrol_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_eqband0_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_eqband1_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_eqband2_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_eqband3_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_eqband4_reg(struct axd_cmd *cmd, unsigned int pipe); + +unsigned int axd_get_output_eq_power_reg_ch0_3(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); +unsigned int axd_get_output_eq_power_reg_ch4_7(struct axd_cmd *cmd, + unsigned int pipe, unsigned int band); + +unsigned int axd_get_output_dcpp_control_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_max_delay_samples_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_ctrl_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_band_ctrl_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_delay_samples_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_output_volume_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_passthrough_gain_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_inverse_passthrough_gain_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_bass_shelf_shift_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_bass_shelf_a0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_bass_shelf_a1_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_bass_shelf_a2_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_bass_shelf_b0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_bass_shelf_b1_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_treble_shelf_shift_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_treble_shelf_a0_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_treble_shelf_a1_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_treble_shelf_a2_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_treble_shelf_b0_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_treble_shelf_b1_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_gain_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_a0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_a1_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_a2_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_b0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_b1_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_channel_eq_shift_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_subband_input_select_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_output_dcpp_subband_low_pass_filter_a0_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_subband_low_pass_filter_a1_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_subband_low_pass_filter_a2_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_subband_low_pass_filter_b0_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_output_dcpp_subband_low_pass_filter_b1_reg( + struct axd_cmd *cmd, unsigned int pipe); + +unsigned int axd_get_decoder_aac_version_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_aac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_aac_profile_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_aac_streamtype_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_aac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_ac3_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ac3_channel_order_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ac3_mode_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_cook_flavour_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_flac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_flac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_flac_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_flac_md5checking_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_mpeg_numchannels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_mpeg_mlchannel_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_wma_playeropt_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_drcsetting_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_peakampref_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_rmsampref_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_peakamptarget_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_rmsamptarget_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_wma_pcmvalidbitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_pcmcontainersize_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_wma_wmaformattag_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmanumchannels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmasamplespersec_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmaaveragebytespersec_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmablockalign_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmavalidbitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmachannelmask_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_wma_wmaencodeoptions_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_pcm_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_pcm_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_pcm_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_pcm_justification_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_ddplus_config_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ddplus_channel_order_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_alac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_alac_depth_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_alac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_alac_framelength_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_alac_maxframebytes_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_alac_avgbitrate_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_sbc_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_sbc_audiomode_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_sbc_blocks_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_sbc_subbands_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_sbc_bitpool_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_sbc_allocationmode_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_decoder_ms11_mode_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ms11_common_config0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ms11_common_config1_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ms11_ddt_config0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ms11_ddc_config0_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_decoder_ms11_ext_pcm_config0_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_encoder_pcm_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_encoder_flac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_bitspersample_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_totalsamples_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_domidsidestereo_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_loosemidsidestereo_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_doexhaustivemodelsearch_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_encoder_flac_minresidualpartitionorder_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_encoder_flac_maxresidualpartitionorder_reg( + struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_encoder_flac_blocksize_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_bytecount_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_samplecount_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_framecount_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_flac_framebytes_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_encoder_alac_channels_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_alac_depth_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_alac_samplerate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_alac_framelength_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_alac_maxframebytes_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_alac_avgbitrate_reg(struct axd_cmd *cmd, + unsigned int pipe); +unsigned int axd_get_encoder_alac_fastmode_reg(struct axd_cmd *cmd, + unsigned int pipe); + +unsigned int axd_get_resample_fin_reg(struct axd_cmd *cmd, unsigned int pipe); +unsigned int axd_get_resample_fout_reg(struct axd_cmd *cmd, unsigned int pipe); + +void axd_cmd_inpipe_init(struct axd_cmd *cmd, unsigned int pipe); +void axd_cmd_outpipe_init(struct axd_cmd *cmd, unsigned int pipe); + +#endif /* AXD_CMDS_INTERNAL_H_ */
On Tue, Oct 28, 2014 at 11:26:25AM +0000, Qais Yousef wrote:
AXD has a lot of registers. These files contain helper functions to access these registers in a readable way.
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org
drivers/char/axd/axd_cmds_codec_internal.c | 58 + drivers/char/axd/axd_cmds_codec_internal.h | 28 + drivers/char/axd/axd_cmds_config.c | 1194 ++++++++++ drivers/char/axd/axd_cmds_decoder_config.c | 1806 ++++++++++++++++ drivers/char/axd/axd_cmds_encoder_config.c | 519 +++++ drivers/char/axd/axd_cmds_info.c | 1409 ++++++++++++ drivers/char/axd/axd_cmds_internal.c | 3237 ++++++++++++++++++++++++++++ drivers/char/axd/axd_cmds_internal.h | 306 +++ 8 files changed, 8557 insertions(+) create mode 100644 drivers/char/axd/axd_cmds_codec_internal.c create mode 100644 drivers/char/axd/axd_cmds_codec_internal.h create mode 100644 drivers/char/axd/axd_cmds_config.c create mode 100644 drivers/char/axd/axd_cmds_decoder_config.c create mode 100644 drivers/char/axd/axd_cmds_encoder_config.c create mode 100644 drivers/char/axd/axd_cmds_info.c create mode 100644 drivers/char/axd/axd_cmds_internal.c create mode 100644 drivers/char/axd/axd_cmds_internal.h
Such a huge amount of code, which by a quick glance of this file should really by ASoC controls and this whole code will vanish. Ah yes some code for decder will remain but then most should still go away if you use the framework
At the moment AXD runs on MIPS cores only. These files provide basic functionality to prepare AXD f/w to bootstrap itself and do low level interrupt/kick when being initialised from a mips core.
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_platform.h | 31 ++++ drivers/char/axd/axd_platform_mips.c | 270 +++++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) create mode 100644 drivers/char/axd/axd_platform.h create mode 100644 drivers/char/axd/axd_platform_mips.c
diff --git a/drivers/char/axd/axd_platform.h b/drivers/char/axd/axd_platform.h new file mode 100644 index 000000000000..1293b632fa67 --- /dev/null +++ b/drivers/char/axd/axd_platform.h @@ -0,0 +1,31 @@ +/* + * 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. + * + * Platform Specific helper functions. + */ +#ifndef AXD_PLATFORM_H_ +#define AXD_PLATFORM_H_ +#include "axd_module.h" + +void axd_platform_init(struct axd_dev *axd); +void axd_platform_set_pc(unsigned long pc); +int axd_platform_start(void); +void axd_platform_stop(void); +unsigned int axd_platform_num_threads(void); +void axd_platform_kick(void); +void axd_platform_irq_ack(void); +void axd_platform_print_regs(void); + +/* + * protect against simultaneous access to shared memory mapped registers area + * between axd and the host + */ +unsigned long axd_platform_lock(void); +void axd_platform_unlock(unsigned long flags); + +#endif /* AXD_PLATFORM_H_ */ diff --git a/drivers/char/axd/axd_platform_mips.c b/drivers/char/axd/axd_platform_mips.c new file mode 100644 index 000000000000..47f02a9fcb85 --- /dev/null +++ b/drivers/char/axd/axd_platform_mips.c @@ -0,0 +1,270 @@ +/* + * 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. + * + * This file implements running AXD as a single VPE along side linux on the same + * core. + */ +#include <linux/device.h> +#include <linux/io.h> + +#include <asm/cpu-features.h> +#include <asm/gic.h> +#include <asm/hazards.h> +#include <asm/mipsregs.h> +#include <asm/mipsmtregs.h> +#include <asm/tlbmisc.h> + +#include "axd_module.h" +#include "axd_platform.h" + +static unsigned int axd_irq; +static unsigned int axd_vpe; + +/* below definition is required to allow axd to be compiled as a module */ +extern unsigned long __weak _gic_base; + +void axd_platform_init(struct axd_dev *axd) +{ + unsigned int val; + unsigned long irqflags; + unsigned long mtflags; + + axd_irq = axd->axd_irq; + axd_vpe = axd->vpe; + + /* + * make sure nothing else on this vpe or another vpe can try to modify + * any of the shared registers below + */ + local_irq_save(irqflags); + mtflags = dvpe(); + + /* EVP = 0, VPC = 1 */ + val = read_c0_mvpcontrol(); + val &= ~MVPCONTROL_EVP; + val |= MVPCONTROL_VPC; + write_c0_mvpcontrol(val); + instruction_hazard(); + + /* prepare TC for setting up */ + settc(axd_vpe); + write_tc_c0_tchalt(1); + + /* make sure no interrupts are pending and exceptions bits are clear */ + write_vpe_c0_cause(0); + write_vpe_c0_status(0); + + /* bind TC to VPE */ + val = read_tc_c0_tcbind(); + val |= (axd_vpe << TCBIND_CURTC_SHIFT) | (axd_vpe << TCBIND_CURVPE_SHIFT); + write_tc_c0_tcbind(val); + + /* VPA = 1, MVP = 1 */ + val = read_vpe_c0_vpeconf0(); + val |= VPECONF0_MVP; + val |= VPECONF0_VPA; + write_vpe_c0_vpeconf0(val); + + /* A = 1, IXMT = 0 */ + val = read_tc_c0_tcstatus(); + val &= ~TCSTATUS_IXMT; + val |= TCSTATUS_A; + write_tc_c0_tcstatus(val); + + /* TE = 1 */ + val = read_vpe_c0_vpecontrol(); + val |= VPECONTROL_TE; + write_vpe_c0_vpecontrol(val); + + /* EVP = 1, VPC = 0 */ + val = read_c0_mvpcontrol(); + val |= MVPCONTROL_EVP; + val &= ~MVPCONTROL_VPC; + write_c0_mvpcontrol(val); + instruction_hazard(); + + evpe(mtflags); + local_irq_restore(irqflags); +} + +static void reset(unsigned int thread) +{ + unsigned int val; + unsigned long irqflags; + unsigned long mtflags; + + local_irq_save(irqflags); + mtflags = dvpe(); + + settc(axd_vpe); + /* first stop TC1 */ + write_tc_c0_tchalt(1); + + /* clear EXL and ERL from TCSTATUS */ + val = read_c0_tcstatus(); + val &= ~(ST0_EXL | ST0_ERL); + write_c0_tcstatus(val); + + evpe(mtflags); + local_irq_restore(irqflags); +} + +void axd_platform_set_pc(unsigned long pc) +{ + unsigned long irqflags; + unsigned long mtflags; + + local_irq_save(irqflags); + mtflags = dvpe(); + + settc(axd_vpe); + write_tc_c0_tcrestart(pc); + + evpe(mtflags); + local_irq_restore(irqflags); +} + +static int thread_control(unsigned int thread, int start) +{ + unsigned long irqflags; + unsigned long mtflags; + + local_irq_save(irqflags); + mtflags = dvpe(); + + settc(axd_vpe); + /* start/stop the thread */ + write_tc_c0_tchalt(!start); + + evpe(mtflags); + local_irq_restore(irqflags); + + return 1; +} + +int axd_platform_start(void) +{ + int thread = 0; + + reset(thread); + if (thread_control(thread, 1)) + return 0; + return -1; +} + +void axd_platform_stop(void) +{ + int thread = 0; + + thread_control(thread, 0); +} + +unsigned int axd_platform_num_threads(void) +{ + return 1; +} + +void axd_platform_kick(void) +{ + unsigned int val; + unsigned long irqflags; + unsigned long mtflags; + + /* + * ensure all writes to shared uncached memory are visible to AXD + * before sending interrupt + */ + wmb(); + + if (axd_irq) { + /* send an interrupt to axd using GIC WEDGE register */ + GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | axd_irq); + return; + } + + /* fallback to sending interrupt at SWT1 */ + + local_irq_save(irqflags); + mtflags = dvpe(); + + settc(axd_vpe); + val = read_vpe_c0_cause(); + val |= CAUSEF_IP1; + write_vpe_c0_cause(val); + + evpe(mtflags); + local_irq_restore(irqflags); +} + +inline unsigned long axd_platform_lock(void) +{ + return dvpe(); +} + +inline void axd_platform_unlock(unsigned long flags) +{ + evpe(flags); +} + +inline void axd_platform_irq_ack(void) +{ +} + +static void print_regs(unsigned int thread) +{ + unsigned long irqflags; + unsigned long mtflags; + + local_irq_save(irqflags); + mtflags = dvpe(); + + settc(thread); + pr_err("PC:\t\t0x%08lX\n", read_tc_c0_tcrestart()); + pr_err("STATUS:\t\t0x%08lX\n", read_vpe_c0_status()); + pr_err("CAUSE:\t\t0x%08lX\n", read_vpe_c0_cause()); + pr_err("EPC:\t\t0x%08lX\n", read_vpe_c0_epc()); + pr_err("EBASE:\t\t0x%08lX\n", read_vpe_c0_ebase()); + pr_err("BADVADDR:\t0x%08lX\n", read_vpe_c0_badvaddr()); + pr_err("CONFIG:\t\t0x%08lX\n", read_vpe_c0_config()); + pr_err("MVPCONTROL:\t0x%08X\n", read_c0_mvpcontrol()); + pr_err("VPECONTROL:\t0x%08lX\n", read_vpe_c0_vpecontrol()); + pr_err("VPECONF0:\t0x%08lX\n", read_vpe_c0_vpeconf0()); + pr_err("TCBIND:\t\t0x%08lX\n", read_tc_c0_tcbind()); + pr_err("TCSTATUS:\t0x%08lX\n", read_tc_c0_tcstatus()); + pr_err("TCHALT:\t\t0x%08lX\n", read_tc_c0_tchalt()); + pr_err("\n"); + pr_err("$0: 0x%08lX\tat: 0x%08lX\tv0: 0x%08lX\tv1: 0x%08lX\n", + mftgpr(0), mftgpr(1), mftgpr(2), mftgpr(3)); + pr_err("a0: 0x%08lX\ta1: 0x%08lX\ta2: 0x%08lX\ta3: 0x%08lX\n", + mftgpr(4), mftgpr(5), mftgpr(6), mftgpr(7)); + pr_err("t0: 0x%08lX\tt1: 0x%08lX\tt2: 0x%08lX\tt3: 0x%08lX\n", + mftgpr(8), mftgpr(9), mftgpr(10), mftgpr(11)); + pr_err("t4: 0x%08lX\tt5: 0x%08lX\tt6: 0x%08lX\tt7: 0x%08lX\n", + mftgpr(12), mftgpr(13), mftgpr(14), mftgpr(15)); + pr_err("s0: 0x%08lX\ts1: 0x%08lX\ts2: 0x%08lX\ts3: 0x%08lX\n", + mftgpr(16), mftgpr(17), mftgpr(18), mftgpr(19)); + pr_err("s4: 0x%08lX\ts5: 0x%08lX\ts6: 0x%08lX\ts7: 0x%08lX\n", + mftgpr(20), mftgpr(21), mftgpr(22), mftgpr(23)); + pr_err("t8: 0x%08lX\tt9: 0x%08lX\tk0: 0x%08lX\tk1: 0x%08lX\n", + mftgpr(24), mftgpr(25), mftgpr(26), mftgpr(27)); + pr_err("gp: 0x%08lX\tsp: 0x%08lX\ts8: 0x%08lX\tra: 0x%08lX\n", + mftgpr(28), mftgpr(29), mftgpr(30), mftgpr(31)); + + evpe(mtflags); + local_irq_restore(irqflags); +} + +void axd_platform_print_regs(void) +{ + int i; + + for (i = 1; i < 2; i++) { + pr_err("Thread %d regs dump\n", i); + print_regs(i); + } +}
lots of AXD configuration is exposed through sysfs, these files create all the necessary nodes to allow setting up AXD
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_sysfs.h | 37 + drivers/char/axd/axd_sysfs_ctrl.c | 539 ++++++++++++++ drivers/char/axd/axd_sysfs_input.c | 406 +++++++++++ drivers/char/axd/axd_sysfs_output.c | 1373 +++++++++++++++++++++++++++++++++++ 4 files changed, 2355 insertions(+) create mode 100644 drivers/char/axd/axd_sysfs.h create mode 100644 drivers/char/axd/axd_sysfs_ctrl.c create mode 100644 drivers/char/axd/axd_sysfs_input.c create mode 100644 drivers/char/axd/axd_sysfs_output.c
diff --git a/drivers/char/axd/axd_sysfs.h b/drivers/char/axd/axd_sysfs.h new file mode 100644 index 000000000000..efab688199da --- /dev/null +++ b/drivers/char/axd/axd_sysfs.h @@ -0,0 +1,37 @@ +/* + * 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 sysfs Support API + */ +#ifndef AXD_SYSFS_H_ +#define AXD_SYSFS_H_ + +#include <linux/stat.h> + +#define CMP_PARAM(str, param) \ + !strncmp((str), (param "="), sizeof(param "=")-1) + +#define PARAM_VALUE(str, param) \ + PARAM_VALUE_WITH_END(str, param, NULL) + +#define PARAM_VALUE_WITH_END(str, param, end) \ + simple_strtol((str)+sizeof(param "=")-1, end, 0) + +#define PARAM_VALUE_ADV(str, param) \ + PARAM_VALUE_WITH_END(str, param, &str) + +#define RD_PERMS (S_IRUSR | S_IRGRP) +#define WR_PERMS (S_IWUSR | S_IWGRP) + +int axd_ctrl_sysfs_add(struct device *dev); +void axd_ctrl_sysfs_remove(struct device *dev); +int axd_input_sysfs_add(struct device *dev); +void axd_input_sysfs_remove(struct device *dev); +int axd_output_sysfs_add(struct device *dev); +void axd_output_sysfs_remove(struct device *dev); +#endif /* AXD_SYSFS_H_ */ diff --git a/drivers/char/axd/axd_sysfs_ctrl.c b/drivers/char/axd/axd_sysfs_ctrl.c new file mode 100644 index 000000000000..660b1d323ce1 --- /dev/null +++ b/drivers/char/axd/axd_sysfs_ctrl.c @@ -0,0 +1,539 @@ +/* + * 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 sysfs Support API for ctrl device + */ +#include <linux/device.h> +#include <linux/kdev_t.h> + +#include "axd_cmds.h" +#include "axd_cmds_internal.h" +#include "axd_module.h" +#include "axd_sysfs.h" + +#define ENABLE_EQCONTROLS 0 + +/* Control Device Sysfs Attributes */ +/* version */ +static ssize_t show_version(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int major, minor, patch; + + axd_cmd_get_version(cmd, &major, &minor, &patch); + return sprintf(buf, "%u.%u.%u\n", major, minor, patch); +} +static DEVICE_ATTR(version, RD_PERMS, show_version, NULL); + +/* num of supported pipes */ +static ssize_t show_pipes(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int inpipes, outpipes; + int ret = axd_cmd_get_num_pipes(cmd, &inpipes, &outpipes); + + if (ret == -1) { + /* No pipes available if error when getting value */ + inpipes = 0; + outpipes = 0; + } + return sprintf(buf, "Number of supported input/output pipes:\n" + "inputs\t= %u\noutputs\t= %u\n", inpipes, outpipes); +} +static DEVICE_ATTR(pipes, RD_PERMS, show_pipes, NULL); + +/* Decoders supported */ +static ssize_t show_decoders(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + char decoders[128]; + + memset(decoders, 0, 128); + axd_cmd_get_decoders(cmd, decoders); + return sprintf(buf, "Supported decoders:\n%s", decoders); +} +static DEVICE_ATTR(decoders, RD_PERMS, show_decoders, NULL); + +/* Encoders supported */ +static ssize_t show_encoders(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + char encoders[128]; + + memset(encoders, 0, 128); + axd_cmd_get_encoders(cmd, encoders); + return sprintf(buf, "Supported encoders:\n%s", encoders); +} +static DEVICE_ATTR(encoders, RD_PERMS, show_encoders, NULL); + +/* Mix/Xbar present */ +static ssize_t show_mix(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int present = axd_cmd_xbar_present(cmd); + + if (present) + return sprintf(buf, "Mix/Xbar present\n"); + return sprintf(buf, + "No Mix/Xbar, 1:1 mapping between input and output pipes\n"); +} +static DEVICE_ATTR(mix, RD_PERMS, show_mix, NULL); + +#if ENABLE_EQCONTROLS +/* Mixer EQ Enable/Disable Control */ +static ssize_t show_eqenabled(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int enable = axd_cmd_mixer_get_eqenabled(cmd, 0); + + return sprintf(buf, "%d\n", !!enable); +} +static ssize_t store_eqenabled(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int enable; + + if (!kstrtoint(buf, 0, &enable)) + axd_cmd_mixer_set_eqenabled(cmd, 0, enable); + return count; +} +static DEVICE_ATTR(eqenabled, RD_PERMS | WR_PERMS, show_eqenabled, + store_eqenabled); + +/* Mixer EQ Master Gain Control */ +static ssize_t show_eqmastergain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + axd_cmd_mixer_get_eqmastergain(cmd, 0, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqmastergain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_mixer_set_eqmastergain(cmd, 0, gain); + return count; +} +static DEVICE_ATTR(eqmastergain, RD_PERMS | WR_PERMS, show_eqmastergain, + store_eqmastergain); + +/* Mixer EQ Band0 Gain Control */ +static ssize_t show_eqband0gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + axd_cmd_mixer_get_eqband0gain(cmd, 0, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband0gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_mixer_set_eqband0gain(cmd, 0, gain); + return count; +} +static DEVICE_ATTR(eqband0gain, RD_PERMS | WR_PERMS, show_eqband0gain, + store_eqband0gain); + +/* Mixer EQ Band1 Gain Control */ +static ssize_t show_eqband1gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + axd_cmd_mixer_get_eqband1gain(cmd, 0, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband1gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_mixer_set_eqband1gain(cmd, 0, gain); + return count; +} +static DEVICE_ATTR(eqband1gain, RD_PERMS | WR_PERMS, show_eqband1gain, + store_eqband1gain); + +/* Mixer EQ Band2 Gain Control */ +static ssize_t show_eqband2gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + axd_cmd_mixer_get_eqband2gain(cmd, 0, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband2gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_mixer_set_eqband2gain(cmd, 0, gain); + return count; +} +static DEVICE_ATTR(eqband2gain, RD_PERMS | WR_PERMS, show_eqband2gain, + store_eqband2gain); + +/* Mixer EQ Band3 Gain Control */ +static ssize_t show_eqband3gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + axd_cmd_mixer_get_eqband3gain(cmd, 0, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband3gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_mixer_set_eqband3gain(cmd, 0, gain); + return count; +} +static DEVICE_ATTR(eqband3gain, RD_PERMS | WR_PERMS, show_eqband3gain, + store_eqband3gain); + +/* Mixer EQ Band4 Gain Control */ +static ssize_t show_eqband4gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + axd_cmd_mixer_get_eqband4gain(cmd, 0, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband4gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_mixer_set_eqband4gain(cmd, 0, gain); + return count; +} +static DEVICE_ATTR(eqband4gain, RD_PERMS | WR_PERMS, show_eqband4gain, + store_eqband4gain); +#endif + +/* Enable/Disable watchdog timer */ +static ssize_t show_watchdog(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + + if (cmd->watchdogenabled) + return sprintf(buf, "enabled\n"); + return sprintf(buf, "disabled\n"); +} +static ssize_t store_watchdog(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int wd_enabled; + + if (!kstrtoint(buf, 0, &wd_enabled)) + cmd->watchdogenabled = wd_enabled; + return count; +} +static DEVICE_ATTR(watchdog, + RD_PERMS | WR_PERMS, show_watchdog, store_watchdog); + +/* + * Enable/Disable discarding output buffers on close without EOS reached. + * + * By default this is enabled, which means that all pending buffers will be + * discarded if an output device is closed before EOS is reached. + * When disabled, opening the output device after closing it before EOS is + * reached will resume from where it stopped. + */ +static ssize_t show_discard(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + + if (axd_get_flag(&cmd->discard_flg)) + return sprintf(buf, "enabled\n"); + return sprintf(buf, "disabled\n"); +} +static ssize_t store_discard(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int discard_flg; + + if (!kstrtouint(buf, 0, &discard_flg)) + axd_set_flag(&cmd->discard_flg, discard_flg); + return count; +} +static DEVICE_ATTR(discard, RD_PERMS | WR_PERMS, show_discard, store_discard); + +/* + * Reset Pipe command, stops and flushes the stream, needed in scenarios + * where the stream doesn't get automatically closed, for example the mixed + * output, or if an input gets opened and then closed without its output being + * opened, so the end flag on close never gets seen and the pipe remains in an + * active state. + */ + +static ssize_t store_reset_pipe(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int pipe; + + if (!kstrtouint(buf, 0, &pipe)) + axd_cmd_outpipe_reset(cmd, pipe); + return count; +} +static DEVICE_ATTR(reset_pipe, WR_PERMS, NULL, store_reset_pipe); + +/* + * Set axd debug mask which can be helpful for reporting firmware problems + */ +static ssize_t show_debug_mask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int mask = 0; + + axd_read_reg(cmd, AXD_REG_DEBUG_MASK, &mask); + return sprintf(buf, "0x%08x\n", mask); +} +static ssize_t store_debug_mask(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int mask; + + if (!kstrtouint(buf, 0, &mask)) + axd_write_reg(cmd, AXD_REG_DEBUG_MASK, mask); + return count; +} +static DEVICE_ATTR(debug_mask, + RD_PERMS | WR_PERMS, show_debug_mask, store_debug_mask); + +static ssize_t show_sync_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int sync_mode = 0; + + axd_read_reg(cmd, AXD_REG_SYNC_MODE, &sync_mode); + switch (sync_mode) { + case 0: + return sprintf(buf, "open_loop (%u)\n", sync_mode); + case 1: + return sprintf(buf, "fixed_frequency (%u)\n", sync_mode); + case 2: + return sprintf(buf, "network_ts (%u)\n", sync_mode); + default: + return sprintf(buf, "unknown (%u)\n", sync_mode); + } +} +static ssize_t store_sync_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int sync_mode; + + if (!kstrtouint(buf, 0, &sync_mode)) + axd_write_reg(cmd, AXD_REG_SYNC_MODE, sync_mode); + return count; +} +static DEVICE_ATTR(sync_mode, + RD_PERMS | WR_PERMS, show_sync_mode, store_sync_mode); + +static ssize_t show_timestamps_out(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + + if (axd_get_flag(&axd->timestamps_out_flg)) + return sprintf(buf, "enabled\n"); + return sprintf(buf, "disabled\n"); +} +static ssize_t store_timestamps_out(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + int enabled; + + if (!kstrtoint(buf, 0, &enabled)) { + struct axd_dev *axd = container_of(cmd, struct axd_dev, cmd); + + axd_set_flag(&axd->timestamps_out_flg, enabled); + } + return count; +} +static DEVICE_ATTR(timestamps_out, + RD_PERMS | WR_PERMS, show_timestamps_out, store_timestamps_out); + +int axd_ctrl_sysfs_add(struct device *dev) +{ +#if ENABLE_EQCONTROLS + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; +#endif + int ret; + + ret = device_create_file(dev, &dev_attr_version); + if (ret) + goto version_err; + ret = device_create_file(dev, &dev_attr_pipes); + if (ret) + goto pipes_err; + ret = device_create_file(dev, &dev_attr_decoders); + if (ret) + goto decoders_err; + ret = device_create_file(dev, &dev_attr_encoders); + if (ret) + goto encoders_err; + ret = device_create_file(dev, &dev_attr_mix); + if (ret) + goto mix_err; +#if ENABLE_EQCONTROLS + if (axd_cmd_xbar_present(cmd)) { + ret = device_create_file(dev, &dev_attr_eqenabled); + if (ret) + goto eqenabled_err; + ret = device_create_file(dev, &dev_attr_eqmastergain); + if (ret) + goto eqmastergain_err; + ret = device_create_file(dev, &dev_attr_eqband0gain); + if (ret) + goto eqband0gain_err; + ret = device_create_file(dev, &dev_attr_eqband1gain); + if (ret) + goto eqband1gain_err; + ret = device_create_file(dev, &dev_attr_eqband2gain); + if (ret) + goto eqband2gain_err; + ret = device_create_file(dev, &dev_attr_eqband3gain); + if (ret) + goto eqband3gain_err; + ret = device_create_file(dev, &dev_attr_eqband4gain); + if (ret) + goto eqband4gain_err; + } +#endif + ret = device_create_file(dev, &dev_attr_watchdog); + if (ret) + goto watchdog_err; + ret = device_create_file(dev, &dev_attr_discard); + if (ret) + goto discard_err; + ret = device_create_file(dev, &dev_attr_reset_pipe); + if (ret) + goto reset_pipe_err; + ret = device_create_file(dev, &dev_attr_debug_mask); + if (ret) + goto debug_mask_err; + ret = device_create_file(dev, &dev_attr_sync_mode); + if (ret) + goto sync_mode_err; + ret = device_create_file(dev, &dev_attr_timestamps_out); + if (ret) + goto timestamps_out_err; + return 0; + +timestamps_out_err: + device_remove_file(dev, &dev_attr_sync_mode); +sync_mode_err: + device_remove_file(dev, &dev_attr_debug_mask); +debug_mask_err: + device_remove_file(dev, &dev_attr_reset_pipe); +reset_pipe_err: + device_remove_file(dev, &dev_attr_discard); +discard_err: + device_remove_file(dev, &dev_attr_watchdog); +watchdog_err: +#if ENABLE_EQCONTROLS + device_remove_file(dev, &dev_attr_eqband4gain); +eqband4gain_err: + device_remove_file(dev, &dev_attr_eqband3gain); +eqband3gain_err: + device_remove_file(dev, &dev_attr_eqband2gain); +eqband2gain_err: + device_remove_file(dev, &dev_attr_eqband1gain); +eqband1gain_err: + device_remove_file(dev, &dev_attr_eqband0gain); +eqband0gain_err: + device_remove_file(dev, &dev_attr_eqmastergain); +eqmastergain_err: + device_remove_file(dev, &dev_attr_eqenabled); +eqenabled_err: +#endif + device_remove_file(dev, &dev_attr_mix); +mix_err: + device_remove_file(dev, &dev_attr_encoders); +encoders_err: + device_remove_file(dev, &dev_attr_decoders); +decoders_err: + device_remove_file(dev, &dev_attr_pipes); +pipes_err: + device_remove_file(dev, &dev_attr_version); +version_err: + return ret; +} + +void axd_ctrl_sysfs_remove(struct device *dev) +{ + if (!dev) + return; + device_remove_file(dev, &dev_attr_timestamps_out); + device_remove_file(dev, &dev_attr_sync_mode); + device_remove_file(dev, &dev_attr_debug_mask); + device_remove_file(dev, &dev_attr_reset_pipe); + device_remove_file(dev, &dev_attr_discard); + device_remove_file(dev, &dev_attr_watchdog); +#if ENABLE_EQCONTROLS + device_remove_file(dev, &dev_attr_eqband4gain); + device_remove_file(dev, &dev_attr_eqband3gain); + device_remove_file(dev, &dev_attr_eqband2gain); + device_remove_file(dev, &dev_attr_eqband1gain); + device_remove_file(dev, &dev_attr_eqband0gain); + device_remove_file(dev, &dev_attr_eqmastergain); + device_remove_file(dev, &dev_attr_eqenabled); +#endif + device_remove_file(dev, &dev_attr_mix); + device_remove_file(dev, &dev_attr_encoders); + device_remove_file(dev, &dev_attr_decoders); + device_remove_file(dev, &dev_attr_pipes); + device_remove_file(dev, &dev_attr_version); +} diff --git a/drivers/char/axd/axd_sysfs_input.c b/drivers/char/axd/axd_sysfs_input.c new file mode 100644 index 000000000000..c4beeb4005fc --- /dev/null +++ b/drivers/char/axd/axd_sysfs_input.c @@ -0,0 +1,406 @@ +/* + * 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 sysfs Support API for input device + */ +#include <linux/device.h> +#include <linux/kdev_t.h> + +#include "axd_cmds.h" +#include "axd_hdr.h" +#include "axd_module.h" +#include "axd_sysfs.h" + +/* Input Device Sysfs Attributes */ +/* Enable/Disable */ +static ssize_t show_enabled(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int enabled = axd_cmd_input_get_enabled(cmd, pipe); + + if (enabled) + return sprintf(buf, "enabled\n"); + return sprintf(buf, "disabled\n"); +} +static ssize_t store_enabled(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int enable = buf[0] != '0'; + + axd_cmd_input_set_enabled(cmd, pipe, enable); + return count; +} +static DEVICE_ATTR(enabled, RD_PERMS, show_enabled, store_enabled); + +/* Source Control */ +static ssize_t show_source(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + char source[32]; + + memset(source, 0, 32); + axd_cmd_input_get_source(cmd, pipe, source); + return sprintf(buf, "%s", source); +} +static ssize_t store_source(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int source; + + if (!kstrtoint(buf, 0, &source)) + axd_cmd_input_set_source(cmd, pipe, source); + return count; +} +static DEVICE_ATTR(source, RD_PERMS | WR_PERMS, show_source, store_source); + +/* Codec Control */ +static ssize_t show_codec(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + char codec[32]; + + memset(codec, 0, 32); + axd_cmd_input_get_codec(cmd, pipe, codec); + return sprintf(buf, "%s", codec); +} +static ssize_t store_codec(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int codec; + + if (!kstrtoint(buf, 0, &codec)) + axd_cmd_input_set_codec(cmd, pipe, codec); + return count; +} +static DEVICE_ATTR(codec, RD_PERMS | WR_PERMS, show_codec, store_codec); + +/* Gain Control */ +static ssize_t show_gain(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int gain; + + axd_cmd_input_get_gain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_input_set_gain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(gain, RD_PERMS | WR_PERMS, show_gain, store_gain); + +/* Mute Control */ +static ssize_t show_mute(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int muted; + + axd_cmd_input_get_mute(cmd, pipe, &muted); + return sprintf(buf, "%d\n", !!muted); +} +static ssize_t store_mute(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int mute; + + if (!kstrtoint(buf, 0, &mute)) + axd_cmd_input_set_mute(cmd, pipe, mute); + return count; +} +static DEVICE_ATTR(mute, RD_PERMS | WR_PERMS, show_mute, store_mute); + +/* UpMix Control */ +static ssize_t show_upmix(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + char upmix[32]; + + axd_cmd_input_get_upmix(cmd, pipe, upmix); + return sprintf(buf, "%s", upmix); +} +static ssize_t store_upmix(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + int upmix; + + if (!kstrtoint(buf, 0, &upmix)) + axd_cmd_input_set_upmix(cmd, pipe, upmix); + return count; +} +static DEVICE_ATTR(upmix, RD_PERMS | WR_PERMS, show_upmix, store_upmix); + +/* Decoder Config */ +static ssize_t show_decoder_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + + axd_cmd_input_get_decoder_config(cmd, pipe, buf); + return strlen(buf)+1; +} +static ssize_t store_decoder_config(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + + axd_cmd_input_set_decoder_config(cmd, pipe, buf); + return count; +} +static DEVICE_ATTR(decoderConfig, RD_PERMS | WR_PERMS, show_decoder_config, + store_decoder_config); + +/* Abort blocked write */ +static ssize_t store_abort_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + + axd_cmd_send_buffer_abort(cmd, pipe); + return count; +} +static DEVICE_ATTR(abortWrite, WR_PERMS, NULL, store_abort_write); + +/*Resampler Frequencies*/ +static ssize_t show_resampler_fin(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + u32 fin = axd_cmd_info_get_resampler_fin(cmd, pipe); + + return sprintf(buf, "%d\n", fin); +} +static DEVICE_ATTR(resampler_fin, RD_PERMS, show_resampler_fin, NULL); + +static ssize_t show_resampler_fout(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + u32 fout = axd_cmd_info_get_resampler_fout(cmd, pipe); + + return sprintf(buf, "%d\n", fout); +} +static ssize_t store_resampler_fout(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + unsigned int fout; + + if (!kstrtouint(buf, 0, &fout)) + axd_cmd_info_set_resampler_fout(cmd, pipe, fout); + return count; +} +static DEVICE_ATTR(resampler_fout, RD_PERMS | WR_PERMS, show_resampler_fout, + store_resampler_fout); + +static ssize_t show_buffer_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + + return sprintf(buf, "%dKiB\n", axd_pipe->buf_size / 1024); +} +static ssize_t store_buffer_size(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + struct axd_pipe *axd_pipe = &cmd->in_pipes[pipe]; + unsigned int buf_size = 0; + unsigned long total_size = axd_cmd_get_datain_size(cmd); + unsigned long num_buffers; + + if (!kstrtouint(buf, 0, &buf_size)) + buf_size *= 1024; + + /* + * if valid, the change will take effect the next time the user opens + * the pipe + */ + if (buf_size) { + num_buffers = total_size / (cmd->num_inputs * buf_size); + if (num_buffers) + axd_pipe->buf_size = buf_size; + } + + return count; +} +static DEVICE_ATTR(buffer_size, RD_PERMS | WR_PERMS, show_buffer_size, + store_buffer_size); + +static ssize_t show_buffer_occupancy(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + unsigned int bo = axd_cmd_input_get_buffer_occupancy(cmd, pipe); + + return sprintf(buf, "%u%%\n", bo); +} +static ssize_t store_buffer_occupancy(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + unsigned int bo; + + if (!kstrtouint(buf, 0, &bo) && bo >= 0 && bo <= 100) { + /* Valid */ + axd_cmd_input_set_buffer_occupancy(cmd, pipe, bo); + } + return count; +} +static DEVICE_ATTR(buffer_occupancy, RD_PERMS | WR_PERMS, show_buffer_occupancy, + store_buffer_occupancy); + +int axd_input_sysfs_add(struct device *dev) +{ + int ret; + + ret = device_create_file(dev, &dev_attr_enabled); + if (ret) + goto enabled_err; + ret = device_create_file(dev, &dev_attr_source); + if (ret) + goto source_err; + ret = device_create_file(dev, &dev_attr_codec); + if (ret) + goto codec_err; + ret = device_create_file(dev, &dev_attr_gain); + if (ret) + goto gain_err; + ret = device_create_file(dev, &dev_attr_mute); + if (ret) + goto mute_err; + ret = device_create_file(dev, &dev_attr_upmix); + if (ret) + goto upmix_err; + ret = device_create_file(dev, &dev_attr_decoderConfig); + if (ret) + goto decoder_config_err; + ret = device_create_file(dev, &dev_attr_abortWrite); + if (ret) + goto abort_write_err; + ret = device_create_file(dev, &dev_attr_resampler_fin); + if (ret) + goto resampler_fin_err; + ret = device_create_file(dev, &dev_attr_resampler_fout); + if (ret) + goto resampler_fout_err; + ret = device_create_file(dev, &dev_attr_buffer_size); + if (ret) + goto buffer_size_err; + ret = device_create_file(dev, &dev_attr_buffer_occupancy); + if (ret) + goto buffer_occupancy_err; + + + return 0; + +buffer_occupancy_err: + device_remove_file(dev, &dev_attr_buffer_size); +buffer_size_err: + device_remove_file(dev, &dev_attr_resampler_fout); +resampler_fout_err: + device_remove_file(dev, &dev_attr_resampler_fin); +resampler_fin_err: + device_remove_file(dev, &dev_attr_abortWrite); +abort_write_err: + device_remove_file(dev, &dev_attr_decoderConfig); +decoder_config_err: + device_remove_file(dev, &dev_attr_upmix); +upmix_err: + device_remove_file(dev, &dev_attr_mute); +mute_err: + device_remove_file(dev, &dev_attr_gain); +gain_err: + device_remove_file(dev, &dev_attr_codec); +codec_err: + device_remove_file(dev, &dev_attr_source); +source_err: + device_remove_file(dev, &dev_attr_enabled); +enabled_err: + return ret; +} + +void axd_input_sysfs_remove(struct device *dev) +{ + if (!dev) + return; + device_remove_file(dev, &dev_attr_buffer_occupancy); + device_remove_file(dev, &dev_attr_buffer_size); + device_remove_file(dev, &dev_attr_resampler_fout); + device_remove_file(dev, &dev_attr_resampler_fin); + device_remove_file(dev, &dev_attr_abortWrite); + device_remove_file(dev, &dev_attr_decoderConfig); + device_remove_file(dev, &dev_attr_upmix); + device_remove_file(dev, &dev_attr_mute); + device_remove_file(dev, &dev_attr_gain); + device_remove_file(dev, &dev_attr_codec); + device_remove_file(dev, &dev_attr_source); + device_remove_file(dev, &dev_attr_enabled); +} diff --git a/drivers/char/axd/axd_sysfs_output.c b/drivers/char/axd/axd_sysfs_output.c new file mode 100644 index 000000000000..d89eb089a54d --- /dev/null +++ b/drivers/char/axd/axd_sysfs_output.c @@ -0,0 +1,1373 @@ +/* + * 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 sysfs Support API for output device + */ +#include <linux/device.h> +#include <linux/kdev_t.h> + +#include "axd_cmds.h" +#include "axd_hdr.h" +#include "axd_module.h" +#include "axd_sysfs.h" + +#define ENABLE_EQMASTERGAIN 0 + +/* + * The following macros operate on DCPP. They help in hiding the verbosity of + * the function names and make the code more readable and less than 80 char in + * length. + */ +#define DCPP_CMP(param) \ + CMP_PARAM(remaining_config, param) + +/* DCPP set for a channel parameter */ +#define DSET_CHAN(name, param) \ + do { \ + data = PARAM_VALUE_ADV(remaining_config, param); \ + axd_cmd_output_set_dcpp_channel_##name(cmd, pipe, \ + channel, data); \ + } while (0) + +/*DCPP set for a channel parameter with band in args - hence chand */ +#define DSET_CHAND(name, param) \ + do { \ + data = PARAM_VALUE_ADV(remaining_config, param); \ + axd_cmd_output_set_dcpp_channel_##name(cmd, pipe, \ + channel, band, data); \ + } while (0) + +/*DCPP set for a subband parameter */ +#define DSET_SBAND(name, param) \ + do { \ + data = PARAM_VALUE_ADV(remaining_config, param); \ + axd_cmd_output_set_dcpp_subband_##name(cmd, pipe, \ + data); \ + } while (0) + +/*DCPP set for a subband parameter with band in args - hence 2 */ +#define DSET_SBAND2(name, param) \ + do { \ + data = PARAM_VALUE_ADV(remaining_config, param); \ + axd_cmd_output_set_dcpp_subband_##name(cmd, pipe, \ + band, data); \ + } while (0) + +/* Output Device Sysfs Attributes */ +/* Enable/Disable */ +static ssize_t show_enabled(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int enabled = axd_cmd_output_get_enabled(cmd, pipe); + + if (enabled) + return sprintf(buf, "enabled\n"); + return sprintf(buf, "disabled\n"); +} +static ssize_t store_enabled(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int enable = buf[0] != '0'; + + axd_cmd_output_set_enabled(cmd, pipe, enable); + return count; +} +static DEVICE_ATTR(enabled, RD_PERMS, show_enabled, store_enabled); + +/* Sink Control */ +static ssize_t show_sink(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + char sink[32]; + + memset(sink, 0, 32); + axd_cmd_output_get_sink(cmd, pipe, sink); + return sprintf(buf, "%s", sink); +} +static ssize_t store_sink(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int sink; + + if (!kstrtoint(buf, 0, &sink)) + axd_cmd_output_set_sink(cmd, pipe, sink); + return count; +} +static DEVICE_ATTR(sink, RD_PERMS | WR_PERMS, show_sink, store_sink); + +/* Encoder Codec Control */ +static ssize_t show_encoder(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + char encoder[32]; + + memset(encoder, 0, 32); + axd_cmd_output_get_codec(cmd, pipe, encoder); + return sprintf(buf, "%s", encoder); +} +static ssize_t store_encoder(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int encoder; + + if (!kstrtoint(buf, 0, &encoder)) + axd_cmd_output_set_codec(cmd, pipe, encoder); + return count; +} +static DEVICE_ATTR(codec, RD_PERMS | WR_PERMS, show_encoder, store_encoder); + +/* DownMix Control */ +static ssize_t show_downmix(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + char downmix[32]; + + axd_cmd_output_get_downmix(cmd, pipe, downmix); + return sprintf(buf, "%s", downmix); +} +static ssize_t store_downmix(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int downmix; + + if (!kstrtoint(buf, 0, &downmix)) + axd_cmd_output_set_downmix(cmd, pipe, downmix); + return count; +} +static DEVICE_ATTR(downmix, RD_PERMS | WR_PERMS, show_downmix, store_downmix); + +/* EQ Enable/Disable Control */ +static ssize_t show_eqenabled(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int enable = axd_cmd_output_get_eqenabled(cmd, pipe); + + return sprintf(buf, "%d\n", !!enable); +} +static ssize_t store_eqenabled(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int enable; + + if (!kstrtoint(buf, 0, &enable)) + axd_cmd_output_set_eqenabled(cmd, pipe, enable); + return count; +} +static DEVICE_ATTR(eqenabled, RD_PERMS | WR_PERMS, show_eqenabled, + store_eqenabled); + +#if ENABLE_EQMASTERGAIN +/* EQ Master Gain Control */ +static ssize_t show_eqmastergain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + axd_cmd_output_get_eqmastergain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqmastergain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_output_set_eqmastergain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(eqmastergain, RD_PERMS | WR_PERMS, show_eqmastergain, + store_eqmastergain); +#endif + +/* EQ Band0 Gain Control */ +static ssize_t show_eqband0gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + axd_cmd_output_get_eqband0gain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband0gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_output_set_eqband0gain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(eqband0gain, RD_PERMS | WR_PERMS, show_eqband0gain, + store_eqband0gain); + +/* EQ Band1 Gain Control */ +static ssize_t show_eqband1gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + axd_cmd_output_get_eqband1gain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband1gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_output_set_eqband1gain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(eqband1gain, RD_PERMS | WR_PERMS, show_eqband1gain, + store_eqband1gain); + +/* EQ Band2 Gain Control */ +static ssize_t show_eqband2gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + axd_cmd_output_get_eqband2gain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband2gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_output_set_eqband2gain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(eqband2gain, RD_PERMS | WR_PERMS, show_eqband2gain, + store_eqband2gain); + +/* EQ Band3 Gain Control */ +static ssize_t show_eqband3gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + axd_cmd_output_get_eqband3gain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband3gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_output_set_eqband3gain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(eqband3gain, RD_PERMS | WR_PERMS, show_eqband3gain, + store_eqband3gain); + +/* EQ Band4 Gain Control */ +static ssize_t show_eqband4gain(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + axd_cmd_output_get_eqband4gain(cmd, pipe, &gain); + return sprintf(buf, "gain\t= %d\n", gain); +} +static ssize_t store_eqband4gain(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int gain; + + if (!kstrtoint(buf, 0, &gain)) + axd_cmd_output_set_eqband4gain(cmd, pipe, gain); + return count; +} +static DEVICE_ATTR(eqband4gain, RD_PERMS | WR_PERMS, show_eqband4gain, + store_eqband4gain); + +#define DCPP_ENABLED_PARAM "enabled" +#define DCPP_CHANNELS_PARAM "channels" +#define DCPP_MODE_PARAM "mode" +#define DCPP_EQMODE_PARAM "eq_mode" +#define DCPP_EQBANDS_PARAM "eq_bands" +#define DCPP_MAXDELAYSAMPLES_PARAM "max_delay_samples" +static ssize_t show_dcpp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + ssize_t ret; + ssize_t n = 0; + + ret = sprintf(buf + n, DCPP_ENABLED_PARAM " = %u\n", + axd_cmd_output_get_dcpp_enabled(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CHANNELS_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channels(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_MODE_PARAM " = %u\n", + axd_cmd_output_get_dcpp_mode(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQMODE_PARAM " = %u\n", + axd_cmd_output_get_dcpp_eq_mode(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQBANDS_PARAM " = %u\n", + axd_cmd_output_get_dcpp_eq_bands(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_MAXDELAYSAMPLES_PARAM " = %u\n", + axd_cmd_output_get_dcpp_max_delay_samples(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + return n; +} +static ssize_t store_dcpp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + unsigned int data; + + if (CMP_PARAM(buf, DCPP_ENABLED_PARAM)) { + data = PARAM_VALUE(buf, DCPP_ENABLED_PARAM); + axd_cmd_output_set_dcpp_enabled(cmd, pipe, data); + } else if (CMP_PARAM(buf, DCPP_MODE_PARAM)) { + data = PARAM_VALUE(buf, DCPP_MODE_PARAM); + axd_cmd_output_set_dcpp_mode(cmd, pipe, data); + } else if (CMP_PARAM(buf, DCPP_EQMODE_PARAM)) { + data = PARAM_VALUE(buf, DCPP_EQMODE_PARAM); + axd_cmd_output_set_dcpp_eq_mode(cmd, pipe, data); + } + + return count; +} +static DEVICE_ATTR(dcpp, RD_PERMS | WR_PERMS, show_dcpp, store_dcpp); + +#define DCPP_SHIFT_SUFFIX "_shift" +#define DCPP_GAIN_SUFFIX "_gain" +#define DCPP_A0_SUFFIX "_a0" +#define DCPP_A1_SUFFIX "_a1" +#define DCPP_A2_SUFFIX "_a2" +#define DCPP_B0_SUFFIX "_b0" +#define DCPP_B1_SUFFIX "_b1" +#define DCPP_DELAYSAMPLES_PARAM "delay_samples" +#define DCPP_EQ_OUTPUTVOLUME_PARAM "eq_output_volume" +#define DCPP_EQ_PASSTHROUGHGAIN_PARAM "eq_passthrough_gain" +#define DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM "eq_inverse_passthrough_gain" +#define DCPP_EQ_BAND_PREFIX "eq_band" +#define DCPP_EQ_BAND_GAIN_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_GAIN_SUFFIX +#define DCPP_EQ_BAND_A0_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_A0_SUFFIX +#define DCPP_EQ_BAND_A1_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_A1_SUFFIX +#define DCPP_EQ_BAND_A2_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_A2_SUFFIX +#define DCPP_EQ_BAND_B0_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_B0_SUFFIX +#define DCPP_EQ_BAND_B1_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_B1_SUFFIX +#define DCPP_EQ_BAND_SHIFT_PARAM DCPP_EQ_BAND_PREFIX "%d" DCPP_SHIFT_SUFFIX +#define DCPP_CH_BASS_SHIFT_PARAM "bass_shelf" DCPP_SHIFT_SUFFIX +#define DCPP_CH_BASS_A0_PARAM "bass_shelf" DCPP_A0_SUFFIX +#define DCPP_CH_BASS_A1_PARAM "bass_shelf" DCPP_A1_SUFFIX +#define DCPP_CH_BASS_A2_PARAM "bass_shelf" DCPP_A2_SUFFIX +#define DCPP_CH_BASS_B0_PARAM "bass_shelf" DCPP_B0_SUFFIX +#define DCPP_CH_BASS_B1_PARAM "bass_shelf" DCPP_B1_SUFFIX +#define DCPP_CH_TREBLE_SHIFT_PARAM "treble_shelf" DCPP_SHIFT_SUFFIX +#define DCPP_CH_TREBLE_A0_PARAM "treble_shelf" DCPP_A0_SUFFIX +#define DCPP_CH_TREBLE_A1_PARAM "treble_shelf" DCPP_A1_SUFFIX +#define DCPP_CH_TREBLE_A2_PARAM "treble_shelf" DCPP_A2_SUFFIX +#define DCPP_CH_TREBLE_B0_PARAM "treble_shelf" DCPP_B0_SUFFIX +#define DCPP_CH_TREBLE_B1_PARAM "treble_shelf" DCPP_B1_SUFFIX +static ssize_t show_dcpp_ch(struct device *dev, struct device_attribute *attr, + char *buf, unsigned int channel) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + int i, ret, bands; + ssize_t n = 0; + + bands = axd_cmd_output_get_dcpp_eq_bands(cmd, pipe); + + ret = sprintf(buf + n, DCPP_DELAYSAMPLES_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_delay_samples(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_OUTPUTVOLUME_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_eq_output_volume(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_PASSTHROUGHGAIN_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_eq_passthrough_gain(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_eq_inverse_passthrough_gain(cmd, + pipe, channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_BASS_SHIFT_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_bass_shelf_shift(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_BASS_A0_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_bass_shelf_a0(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_BASS_A1_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_bass_shelf_a1(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_BASS_A2_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_bass_shelf_a2(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_BASS_B0_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_bass_shelf_b0(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_BASS_B1_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_bass_shelf_b1(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_TREBLE_SHIFT_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_treble_shelf_shift(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_TREBLE_A0_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_treble_shelf_a0(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_TREBLE_A1_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_treble_shelf_a1(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_TREBLE_A2_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_treble_shelf_a2(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_TREBLE_B0_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_treble_shelf_b0(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_CH_TREBLE_B1_PARAM " = %u\n", + axd_cmd_output_get_dcpp_channel_treble_shelf_b1(cmd, pipe, + channel)); + if (ret < 0) + return ret; + n += ret; + + for (i = 0; i < bands; i++) { + + ret = sprintf(buf + n, DCPP_EQ_BAND_GAIN_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_gain(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_A0_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_a0(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_A1_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_a1(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_A2_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_a2(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_B0_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_b0(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_B1_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_b1(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_SHIFT_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_channel_eq_shift(cmd, pipe, + channel, i)); + if (ret < 0) + return ret; + n += ret; + } + + return n; +} +static ssize_t store_dcpp_ch(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count, unsigned int channel) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + unsigned int data; + char *remaining_config = (char *)&buf[0]; + + axd_set_flag(&cmd->ctrlbuf_active_flg, 1); + + while (remaining_config[0] != '\0') { + if (DCPP_CMP(DCPP_DELAYSAMPLES_PARAM)) { + DSET_CHAN(delay_samples, DCPP_DELAYSAMPLES_PARAM); + } else if (DCPP_CMP(DCPP_EQ_OUTPUTVOLUME_PARAM)) { + DSET_CHAN(eq_output_volume, DCPP_EQ_OUTPUTVOLUME_PARAM); + } else if (DCPP_CMP(DCPP_EQ_PASSTHROUGHGAIN_PARAM)) { + DSET_CHAN(eq_passthrough_gain, + DCPP_EQ_PASSTHROUGHGAIN_PARAM); + } else if (DCPP_CMP(DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM)) { + DSET_CHAN(eq_inverse_passthrough_gain, + DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM); + } else if (DCPP_CMP(DCPP_CH_BASS_SHIFT_PARAM)) { + DSET_CHAN(bass_shelf_shift, DCPP_CH_BASS_SHIFT_PARAM); + } else if (DCPP_CMP(DCPP_CH_BASS_A0_PARAM)) { + DSET_CHAN(bass_shelf_a0, DCPP_CH_BASS_A0_PARAM); + } else if (DCPP_CMP(DCPP_CH_BASS_A1_PARAM)) { + DSET_CHAN(bass_shelf_a1, DCPP_CH_BASS_A1_PARAM); + } else if (DCPP_CMP(DCPP_CH_BASS_A2_PARAM)) { + DSET_CHAN(bass_shelf_a2, DCPP_CH_BASS_A2_PARAM); + } else if (DCPP_CMP(DCPP_CH_BASS_B0_PARAM)) { + DSET_CHAN(bass_shelf_b0, DCPP_CH_BASS_B0_PARAM); + } else if (DCPP_CMP(DCPP_CH_BASS_B1_PARAM)) { + DSET_CHAN(bass_shelf_b1, DCPP_CH_BASS_B1_PARAM); + } else if (DCPP_CMP(DCPP_CH_TREBLE_SHIFT_PARAM)) { + DSET_CHAN(treble_shelf_shift, + DCPP_CH_TREBLE_SHIFT_PARAM); + } else if (DCPP_CMP(DCPP_CH_TREBLE_A0_PARAM)) { + DSET_CHAN(treble_shelf_a0, DCPP_CH_TREBLE_A0_PARAM); + } else if (DCPP_CMP(DCPP_CH_TREBLE_A1_PARAM)) { + DSET_CHAN(treble_shelf_a1, DCPP_CH_TREBLE_A1_PARAM); + } else if (DCPP_CMP(DCPP_CH_TREBLE_A2_PARAM)) { + DSET_CHAN(treble_shelf_a2, DCPP_CH_TREBLE_A2_PARAM); + } else if (DCPP_CMP(DCPP_CH_TREBLE_B0_PARAM)) { + DSET_CHAN(treble_shelf_b0, DCPP_CH_TREBLE_B0_PARAM); + } else if (DCPP_CMP(DCPP_CH_TREBLE_B1_PARAM)) { + DSET_CHAN(treble_shelf_b1, DCPP_CH_TREBLE_B1_PARAM); + } else { + size_t len = sizeof(DCPP_EQ_BAND_PREFIX)-1; + + if (!strncmp(remaining_config, + DCPP_EQ_BAND_PREFIX, len)) { + unsigned int band; + + /* Skip prefix */ + remaining_config += len; + sscanf(remaining_config, "%u", &band); + + /* Skip band number */ + remaining_config++; + if (band > 9) + remaining_config++; + + if (DCPP_CMP(DCPP_GAIN_SUFFIX)) + DSET_CHAND(eq_gain, DCPP_GAIN_SUFFIX); + else if (DCPP_CMP(DCPP_A0_SUFFIX)) + DSET_CHAND(eq_a0, DCPP_A0_SUFFIX); + else if (DCPP_CMP(DCPP_A1_SUFFIX)) + DSET_CHAND(eq_a1, DCPP_A1_SUFFIX); + else if (DCPP_CMP(DCPP_A2_SUFFIX)) + DSET_CHAND(eq_a2, DCPP_A2_SUFFIX); + else if (DCPP_CMP(DCPP_B0_SUFFIX)) + DSET_CHAND(eq_b0, DCPP_B0_SUFFIX); + else if (DCPP_CMP(DCPP_B1_SUFFIX)) + DSET_CHAND(eq_b1, DCPP_B1_SUFFIX); + else if (DCPP_CMP(DCPP_SHIFT_SUFFIX)) + DSET_CHAND(eq_shift, DCPP_SHIFT_SUFFIX); + } + } + /* Advance pointer */ + remaining_config++; + } + + axd_flush_reg_buf(cmd); + + axd_set_flag(&cmd->ctrlbuf_active_flg, 0); + + return count; +} + +static ssize_t show_dcpp_ch0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_dcpp_ch(dev, attr, buf, 0); +} +static ssize_t store_dcpp_ch0(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_dcpp_ch(dev, attr, buf, count, 0); +} +static DEVICE_ATTR(dcpp_ch0, + RD_PERMS | WR_PERMS, show_dcpp_ch0, store_dcpp_ch0); + +static ssize_t show_dcpp_ch1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_dcpp_ch(dev, attr, buf, 1); +} +static ssize_t store_dcpp_ch1(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return store_dcpp_ch(dev, attr, buf, count, 1); +} +static DEVICE_ATTR(dcpp_ch1, + RD_PERMS | WR_PERMS, show_dcpp_ch1, store_dcpp_ch1); + +#define DCPP_SUBBAND_INPUTCHANNELMASK_PARAM "input_channel_mask" +#define DCPP_SUBBAND_LPF_A0_PARAM "low_pass_filter_a0" +#define DCPP_SUBBAND_LPF_A1_PARAM "low_pass_filter_a1" +#define DCPP_SUBBAND_LPF_A2_PARAM "low_pass_filter_a2" +#define DCPP_SUBBAND_LPF_B0_PARAM "low_pass_filter_b0" +#define DCPP_SUBBAND_LPF_B1_PARAM "low_pass_filter_b1" +static ssize_t show_dcpp_subband(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + ssize_t ret; + ssize_t n = 0; + int i; + int bands = axd_cmd_output_get_dcpp_subband_eq_bands(cmd, pipe); + + ret = sprintf(buf + n, DCPP_ENABLED_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_enabled(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQBANDS_PARAM " = %u\n", bands); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_DELAYSAMPLES_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_delay_samples(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_SUBBAND_INPUTCHANNELMASK_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_input_channel_mask(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_SUBBAND_LPF_A0_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_low_pass_filter_a0(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_SUBBAND_LPF_A1_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_low_pass_filter_a1(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_SUBBAND_LPF_A2_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_low_pass_filter_a2(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_SUBBAND_LPF_B0_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_low_pass_filter_b0(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_SUBBAND_LPF_B1_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_low_pass_filter_b1(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_OUTPUTVOLUME_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_eq_output_volume(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_PASSTHROUGHGAIN_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_eq_passthrough_gain(cmd, pipe)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM " = %u\n", + axd_cmd_output_get_dcpp_subband_eq_inverse_passthrough_gain(cmd, + pipe)); + if (ret < 0) + return ret; + n += ret; + + for (i = 0; i < bands; i++) { + ret = sprintf(buf + n, DCPP_EQ_BAND_GAIN_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_gain(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_A0_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_a0(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_A1_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_a1(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_A2_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_a2(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_B0_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_b0(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_B1_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_b1(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + + ret = sprintf(buf + n, DCPP_EQ_BAND_SHIFT_PARAM " = %u\n", i, + axd_cmd_output_get_dcpp_subband_eq_shift(cmd, pipe, i)); + if (ret < 0) + return ret; + n += ret; + } + + return n; +} +static ssize_t store_dcpp_subband(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + unsigned int data; + char *remaining_config = (char *)&buf[0]; + + axd_set_flag(&cmd->ctrlbuf_active_flg, 1); + + while (remaining_config[0] != '\0') { + if (DCPP_CMP(DCPP_ENABLED_PARAM)) { + DSET_SBAND(enabled, DCPP_ENABLED_PARAM); + } else if (DCPP_CMP(DCPP_DELAYSAMPLES_PARAM)) { + DSET_SBAND(delay_samples, DCPP_DELAYSAMPLES_PARAM); + } else if (DCPP_CMP(DCPP_SUBBAND_INPUTCHANNELMASK_PARAM)) { + DSET_SBAND(input_channel_mask, + DCPP_SUBBAND_INPUTCHANNELMASK_PARAM); + } else if (DCPP_CMP(DCPP_SUBBAND_LPF_A0_PARAM)) { + DSET_SBAND(low_pass_filter_a0, + DCPP_SUBBAND_LPF_A0_PARAM); + } else if (DCPP_CMP(DCPP_SUBBAND_LPF_A1_PARAM)) { + DSET_SBAND(low_pass_filter_a1, + DCPP_SUBBAND_LPF_A1_PARAM); + } else if (DCPP_CMP(DCPP_SUBBAND_LPF_A2_PARAM)) { + DSET_SBAND(low_pass_filter_a2, + DCPP_SUBBAND_LPF_A2_PARAM); + } else if (DCPP_CMP(DCPP_SUBBAND_LPF_B0_PARAM)) { + DSET_SBAND(low_pass_filter_b0, + DCPP_SUBBAND_LPF_B0_PARAM); + } else if (DCPP_CMP(DCPP_SUBBAND_LPF_B1_PARAM)) { + DSET_SBAND(low_pass_filter_b1, + DCPP_SUBBAND_LPF_B1_PARAM); + } else if (DCPP_CMP(DCPP_EQ_OUTPUTVOLUME_PARAM)) { + DSET_SBAND(eq_output_volume, + DCPP_EQ_OUTPUTVOLUME_PARAM); + } else if (DCPP_CMP(DCPP_EQ_PASSTHROUGHGAIN_PARAM)) { + DSET_SBAND(eq_passthrough_gain, + DCPP_EQ_PASSTHROUGHGAIN_PARAM); + } else if (DCPP_CMP(DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM)) { + DSET_SBAND(eq_inverse_passthrough_gain, + DCPP_EQ_INVERSEPASSTHROUGHGAIN_PARAM); + } else { + size_t len = sizeof(DCPP_EQ_BAND_PREFIX)-1; + + if (!strncmp(remaining_config, + DCPP_EQ_BAND_PREFIX, len)) { + unsigned int band; + + /* Skip prefix */ + remaining_config += len; + sscanf(remaining_config, "%u", &band); + + /* Skip band number */ + remaining_config++; + if (band > 9) + remaining_config++; + + if (DCPP_CMP(DCPP_GAIN_SUFFIX)) + DSET_SBAND2(eq_gain, DCPP_GAIN_SUFFIX); + else if (DCPP_CMP(DCPP_A0_SUFFIX)) + DSET_SBAND2(eq_a0, DCPP_A0_SUFFIX); + else if (DCPP_CMP(DCPP_A1_SUFFIX)) + DSET_SBAND2(eq_a1, DCPP_A1_SUFFIX); + else if (DCPP_CMP(DCPP_A2_SUFFIX)) + DSET_SBAND2(eq_a2, DCPP_A2_SUFFIX); + else if (DCPP_CMP(DCPP_B0_SUFFIX)) + DSET_SBAND2(eq_b0, DCPP_B0_SUFFIX); + else if (DCPP_CMP(DCPP_B1_SUFFIX)) + DSET_SBAND2(eq_b1, DCPP_B1_SUFFIX); + else if (DCPP_CMP(DCPP_SHIFT_SUFFIX)) + DSET_SBAND2(eq_shift, + DCPP_SHIFT_SUFFIX); + } + } + /* Advance pointer */ + remaining_config++; + } + + axd_flush_reg_buf(cmd); + + axd_set_flag(&cmd->ctrlbuf_active_flg, 0); + + return count; +} +static DEVICE_ATTR(dcpp_subband, + RD_PERMS | WR_PERMS, show_dcpp_subband, store_dcpp_subband); + +/* Abort blocked read */ +static ssize_t store_abort_read(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_INPUT(minor); + + axd_cmd_recv_buffer_abort(cmd, pipe); + return count; +} +static DEVICE_ATTR(abortRead, WR_PERMS, NULL, store_abort_read); + +/* Encoder configuration */ +static ssize_t show_encoder_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_encoder_config(cmd, pipe, buf); + return strlen(buf)+1; +} +static ssize_t store_encoder_config(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_set_encoder_config(cmd, pipe, buf); + return count; +} +static DEVICE_ATTR(encoderConfig, RD_PERMS | WR_PERMS, show_encoder_config, + store_encoder_config); + +/* GEQ power levels */ +static ssize_t show_geq_power_c0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 0); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch0, RD_PERMS , show_geq_power_c0, NULL); + + +static ssize_t show_geq_power_c1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 1); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch1, RD_PERMS , show_geq_power_c1, NULL); + +static ssize_t show_geq_power_c2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 2); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch2, RD_PERMS , show_geq_power_c2, NULL); + +static ssize_t show_geq_power_c3(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 3); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch3, RD_PERMS , show_geq_power_c3, NULL); + +static ssize_t show_geq_power_c4(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 4); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch4, RD_PERMS , show_geq_power_c4, NULL); + +static ssize_t show_geq_power_c5(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 5); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch5, RD_PERMS , show_geq_power_c5, NULL); + +static ssize_t show_geq_power_c6(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 6); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch6, RD_PERMS , show_geq_power_c6, NULL); + +static ssize_t show_geq_power_c7(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + + axd_cmd_output_get_geq_power(cmd, pipe, buf, 7); + return strlen(buf)+1; +} +static DEVICE_ATTR(eqpower_ch7, RD_PERMS , show_geq_power_c7, NULL); + +static ssize_t show_buffer_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + + return sprintf(buf, "%dKiB\n", axd_pipe->buf_size / 1024); +} +static ssize_t store_buffer_size(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + struct axd_pipe *axd_pipe = &cmd->out_pipes[pipe]; + unsigned int buf_size = 0; + unsigned long total_size = axd_cmd_get_dataout_size(cmd); + unsigned long num_buffers; + + if (!kstrtouint(buf, 0, &buf_size)) + buf_size *= 1024; + + /* + * if valid, the change will take effect the next time the user opens + * the pipe + */ + if (buf_size) { + num_buffers = total_size / (cmd->num_outputs * buf_size); + if (num_buffers) + axd_pipe->buf_size = buf_size; + } + + return count; +} +static DEVICE_ATTR(buffer_size, RD_PERMS | WR_PERMS, show_buffer_size, + store_buffer_size); + +/* Mux Output Select */ +static ssize_t show_mux(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + char mux[32]; + + axd_cmd_mixer_get_mux(cmd, pipe, mux); + return sprintf(buf, "%s", mux); +} +static ssize_t store_mux(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data; + unsigned int minor = MINOR(dev->devt); + unsigned int pipe = MINOR_TO_OUTPUT(minor); + int mux; + + if (!kstrtoint(buf, 0, &mux)) + axd_cmd_mixer_set_mux(cmd, pipe, mux); + return count; +} +static DEVICE_ATTR(mux, RD_PERMS | WR_PERMS, show_mux, store_mux); + + +int axd_output_sysfs_add(struct device *dev) +{ + int ret; + + ret = device_create_file(dev, &dev_attr_enabled); + if (ret) + goto enabled_err; + ret = device_create_file(dev, &dev_attr_sink); + if (ret) + goto sink_err; + ret = device_create_file(dev, &dev_attr_downmix); + if (ret) + goto downmix_err; + ret = device_create_file(dev, &dev_attr_codec); + if (ret) + goto codec_err; + ret = device_create_file(dev, &dev_attr_eqenabled); + if (ret) + goto eqenabled_err; +#if ENABLE_EQMASTERGAIN + ret = device_create_file(dev, &dev_attr_eqmastergain); + if (ret) + goto eqmastergain_err; +#endif + ret = device_create_file(dev, &dev_attr_eqband0gain); + if (ret) + goto eqband0gain_err; + ret = device_create_file(dev, &dev_attr_eqband1gain); + if (ret) + goto eqband1gain_err; + ret = device_create_file(dev, &dev_attr_eqband2gain); + if (ret) + goto eqband2gain_err; + ret = device_create_file(dev, &dev_attr_eqband3gain); + if (ret) + goto eqband3gain_err; + ret = device_create_file(dev, &dev_attr_eqband4gain); + if (ret) + goto eqband4gain_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch0); + if (ret) + goto eqpower_ch0_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch1); + if (ret) + goto eqpower_ch1_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch2); + if (ret) + goto eqpower_ch2_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch3); + if (ret) + goto eqpower_ch3_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch4); + if (ret) + goto eqpower_ch4_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch5); + if (ret) + goto eqpower_ch5_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch6); + if (ret) + goto eqpower_ch6_err; + ret = device_create_file(dev, &dev_attr_eqpower_ch7); + if (ret) + goto eqpower_ch7_err; + ret = device_create_file(dev, &dev_attr_dcpp); + if (ret) + goto dcpp_err; + ret = device_create_file(dev, &dev_attr_dcpp_ch0); + if (ret) + goto dcpp_ch0_err; + ret = device_create_file(dev, &dev_attr_dcpp_ch1); + if (ret) + goto dcpp_ch1_err; + ret = device_create_file(dev, &dev_attr_dcpp_subband); + if (ret) + goto dcpp_subband_err; + ret = device_create_file(dev, &dev_attr_abortRead); + if (ret) + goto abort_read_err; + ret = device_create_file(dev, &dev_attr_encoderConfig); + if (ret) + goto encoder_config_err; + ret = device_create_file(dev, &dev_attr_buffer_size); + if (ret) + goto buffer_size_err; + ret = device_create_file(dev, &dev_attr_mux); + if (ret) + goto mux_err; + return 0; + +mux_err: + device_remove_file(dev, &dev_attr_buffer_size); +buffer_size_err: + device_remove_file(dev, &dev_attr_encoderConfig); +encoder_config_err: + device_remove_file(dev, &dev_attr_abortRead); +abort_read_err: + device_remove_file(dev, &dev_attr_dcpp_subband); +dcpp_subband_err: + device_remove_file(dev, &dev_attr_dcpp_ch1); +dcpp_ch1_err: + device_remove_file(dev, &dev_attr_dcpp_ch0); +dcpp_ch0_err: + device_remove_file(dev, &dev_attr_dcpp); +dcpp_err: + device_remove_file(dev, &dev_attr_eqpower_ch7); +eqpower_ch7_err: + device_remove_file(dev, &dev_attr_eqpower_ch6); +eqpower_ch6_err: + device_remove_file(dev, &dev_attr_eqpower_ch5); +eqpower_ch5_err: + device_remove_file(dev, &dev_attr_eqpower_ch4); +eqpower_ch4_err: + device_remove_file(dev, &dev_attr_eqpower_ch3); +eqpower_ch3_err: + device_remove_file(dev, &dev_attr_eqpower_ch2); +eqpower_ch2_err: + device_remove_file(dev, &dev_attr_eqpower_ch1); +eqpower_ch1_err: + device_remove_file(dev, &dev_attr_eqpower_ch0); +eqpower_ch0_err: + device_remove_file(dev, &dev_attr_eqband4gain); +eqband4gain_err: + device_remove_file(dev, &dev_attr_eqband3gain); +eqband3gain_err: + device_remove_file(dev, &dev_attr_eqband2gain); +eqband2gain_err: + device_remove_file(dev, &dev_attr_eqband1gain); +eqband1gain_err: + device_remove_file(dev, &dev_attr_eqband0gain); +eqband0gain_err: +#if ENABLE_EQMASTERGAIN + device_remove_file(dev, &dev_attr_eqmastergain); +eqmastergain_err: +#endif + device_remove_file(dev, &dev_attr_eqenabled); +eqenabled_err: + device_remove_file(dev, &dev_attr_codec); +codec_err: + device_remove_file(dev, &dev_attr_downmix); +downmix_err: + device_remove_file(dev, &dev_attr_sink); +sink_err: + device_remove_file(dev, &dev_attr_enabled); +enabled_err: + return ret; +} + +void axd_output_sysfs_remove(struct device *dev) +{ + if (!dev) + return; + device_remove_file(dev, &dev_attr_mux); + device_remove_file(dev, &dev_attr_buffer_size); + device_remove_file(dev, &dev_attr_encoderConfig); + device_remove_file(dev, &dev_attr_abortRead); + device_remove_file(dev, &dev_attr_dcpp_subband); + device_remove_file(dev, &dev_attr_dcpp_ch1); + device_remove_file(dev, &dev_attr_dcpp_ch0); + device_remove_file(dev, &dev_attr_dcpp); + device_remove_file(dev, &dev_attr_eqpower_ch7); + device_remove_file(dev, &dev_attr_eqpower_ch6); + device_remove_file(dev, &dev_attr_eqpower_ch5); + device_remove_file(dev, &dev_attr_eqpower_ch4); + device_remove_file(dev, &dev_attr_eqpower_ch3); + device_remove_file(dev, &dev_attr_eqpower_ch2); + device_remove_file(dev, &dev_attr_eqpower_ch1); + device_remove_file(dev, &dev_attr_eqpower_ch0); + device_remove_file(dev, &dev_attr_eqband4gain); + device_remove_file(dev, &dev_attr_eqband3gain); + device_remove_file(dev, &dev_attr_eqband2gain); + device_remove_file(dev, &dev_attr_eqband1gain); + device_remove_file(dev, &dev_attr_eqband0gain); +#if ENABLE_EQMASTERGAIN + device_remove_file(dev, &dev_attr_eqmastergain); +#endif + device_remove_file(dev, &dev_attr_eqenabled); + device_remove_file(dev, &dev_attr_codec); + device_remove_file(dev, &dev_attr_downmix); + device_remove_file(dev, &dev_attr_sink); + device_remove_file(dev, &dev_attr_enabled); +}
On Tue, Oct 28, 2014 at 11:26:27AM +0000, Qais Yousef wrote:
lots of AXD configuration is exposed through sysfs, these files create all the necessary nodes to allow setting up AXD
Yet you don't document that interface through the required Documentation/ABI/ files? Why not?
On 10/28/2014 02:11 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 11:26:27AM +0000, Qais Yousef wrote:
lots of AXD configuration is exposed through sysfs, these files create all the necessary nodes to allow setting up AXD
Yet you don't document that interface through the required Documentation/ABI/ files? Why not?
Sorry I didn't realise I need to do that. I'll fix that in the next series.
Thanks, Qais
On Tue, Oct 28, 2014 at 11:26:27AM +0000, Qais Yousef wrote:
+/* Control Device Sysfs Attributes */ +/* version */ +static ssize_t show_version(struct device *dev, struct device_attribute *attr,
char *buf)
+{
- struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data;
- int major, minor, patch;
- axd_cmd_get_version(cmd, &major, &minor, &patch);
- return sprintf(buf, "%u.%u.%u\n", major, minor, patch);
+} +static DEVICE_ATTR(version, RD_PERMS, show_version, NULL);
DEVICE_ATTR_RO() please. Same for all other instances, you should never use DEVICE_ATTR() anymore.
thanks,
greg k-h
On 10/28/2014 02:12 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 11:26:27AM +0000, Qais Yousef wrote:
+/* Control Device Sysfs Attributes */ +/* version */ +static ssize_t show_version(struct device *dev, struct device_attribute *attr,
char *buf)
+{
- struct axd_cmd *cmd = (struct axd_cmd *)dev->platform_data;
- int major, minor, patch;
- axd_cmd_get_version(cmd, &major, &minor, &patch);
- return sprintf(buf, "%u.%u.%u\n", major, minor, patch);
+} +static DEVICE_ATTR(version, RD_PERMS, show_version, NULL);
DEVICE_ATTR_RO() please. Same for all other instances, you should never use DEVICE_ATTR() anymore.
thanks,
greg k-h
OK. I missed the transition to the new macro. I'll fix that.
Thanks, Qais
On Tue, Oct 28, 2014 at 11:26:27AM +0000, Qais Yousef wrote:
lots of AXD configuration is exposed through sysfs, these files create all the necessary nodes to allow setting up AXD
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org
These *should* be alsa kcontrols and compressed device capabilities.
so:
NAKed-by: Vinod Koul vinod.koul@intel.com
ts (time stamp) interface is required when axd is to perform synchronisation. since the time stamp hardware is SoC dependent, this file provides the interface that can be implemented by a different driver to do the necessary time stamp translation to perform synchronisation inside AXD
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/axd/axd_ts_driver.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 drivers/char/axd/axd_ts_driver.h
diff --git a/drivers/char/axd/axd_ts_driver.h b/drivers/char/axd/axd_ts_driver.h new file mode 100644 index 000000000000..2b99ad4a7adc --- /dev/null +++ b/drivers/char/axd/axd_ts_driver.h @@ -0,0 +1,24 @@ +/* + * 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. + * + * AXD timestamp driver interface. + */ +#include <linux/types.h> + +/* + * Called at the beginning of a stream to set the fixed reference point in time + * for the stream. This should not be implemented if the reference point should + * not be altered in this way, and is managed through other means. + */ +void __attribute__((weak)) axd_ts_reset(void); + +/* + * Adjusts the provided timestamp from the source to destination formats. + * @return Non-zero for failure. + */ +int __attribute__((weak)) axd_ts_adjust(u64 *ts);
Now all necessary files are added, allow AXD to be selected through Kconfig and compiled.
NOTE: at the moment AXD can only work on MIPS but this restrictions should be lifted in the future as it evolves.
Signed-off-by: Qais Yousef qais.yousef@imgtec.com Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: alsa-devel@alsa-project.org --- drivers/char/Kconfig | 2 ++ drivers/char/Makefile | 2 ++ drivers/char/axd/Kconfig | 19 +++++++++++++++++++ drivers/char/axd/Makefile | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 drivers/char/axd/Kconfig create mode 100644 drivers/char/axd/Makefile
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index efefd12a0f7b..7800a72cf771 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -602,5 +602,7 @@ config TILE_SROM
source "drivers/char/xillybus/Kconfig"
+source "drivers/char/axd/Kconfig" + endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index d06cde26031b..6c1769d8abf3 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -62,3 +62,5 @@ js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o obj-$(CONFIG_XILLYBUS) += xillybus/ + +obj-$(CONFIG_AXD) += axd/ diff --git a/drivers/char/axd/Kconfig b/drivers/char/axd/Kconfig new file mode 100644 index 000000000000..a1a94b12c04a --- /dev/null +++ b/drivers/char/axd/Kconfig @@ -0,0 +1,19 @@ +config AXD + tristate "AXD Audio Processing IP" + depends on MIPS && COMMON_CLK && CMA + ---help--- + Say Y here if you want to use AXD Audio Processing IP. + +config AXD_DEBUG_DIAG + bool "AXD diagnostics" + depends on AXD + ---help--- + Say Y if you want AXD driver to print some diagnostics after playing + a stream. + +config AXD_DEBUG_VERBOSE + bool "AXD verbose debug" + depends on AXD + ---help--- + Say Y if you want AXD driver to print verbose debug information. + The output is extensive. diff --git a/drivers/char/axd/Makefile b/drivers/char/axd/Makefile new file mode 100644 index 000000000000..3b367721073d --- /dev/null +++ b/drivers/char/axd/Makefile @@ -0,0 +1,20 @@ +obj-$(CONFIG_AXD) := axd.o + +axd-objs = axd_buffers.o \ + axd_cmds.o \ + axd_cmds_codec_internal.o \ + axd_cmds_config.o \ + axd_cmds_decoder_config.o \ + axd_cmds_encoder_config.o \ + axd_cmds_info.o \ + axd_cmds_internal.o \ + axd_cmds_pipes.o \ + axd_hdr.o \ + axd_module.o \ + axd_platform_mips.o \ + axd_sysfs_ctrl.o \ + axd_sysfs_input.o \ + axd_sysfs_output.o + +ccflags-$(CONFIG_AXD_DEBUG_DIAG) += -DDEBUG_DIAG +ccflags-$(CONFIG_AXD_DEBUG_VERBOSE) += -DDEBUG_BUFFERS
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
I added it to drivers/char as this is the best location I could think of at the moment.
drivers/misc?
Regards, Clemens
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
- Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
- Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
- Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
I added it to drivers/char as this is the best location I could think of at the moment.
drivers/misc?
Yeah could do if there's consensus.
Thanks, Qais
Regards, Clemens
On Tue, Oct 28, 2014 at 01:18:28PM +0000, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
So instead you created a one-off api just for this hardware? Ick, no, please work with the audio developers to incorporate it into the standard Linux audio apis so that everyone can benifit and not require special userspace programs to drive this hardware.
thanks,
greg k-h
On 10/28/2014 02:13 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 01:18:28PM +0000, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
So instead you created a one-off api just for this hardware? Ick, no, please work with the audio developers to incorporate it into the standard Linux audio apis so that everyone can benifit and not require special userspace programs to drive this hardware.
thanks,
greg k-h
OK. I'll wait to hear from alsa developers to see the extent of work required. I can't see it being trivial though. Would it be possible for this to be accepted into staging until this is resolved?
Thanks, Qais
On Tue, Oct 28, 2014 at 03:05:16PM +0000, Qais Yousef wrote:
On 10/28/2014 02:13 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 01:18:28PM +0000, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
So instead you created a one-off api just for this hardware? Ick, no, please work with the audio developers to incorporate it into the standard Linux audio apis so that everyone can benifit and not require special userspace programs to drive this hardware.
thanks,
greg k-h
OK. I'll wait to hear from alsa developers to see the extent of work required. I can't see it being trivial though. Would it be possible for this to be accepted into staging until this is resolved?
If you are willing to abide by the staging rules: - incremental patches only doing one thing at a time - never break the build - constantly moving forward to getting merged (i.e. no new features being added)
I think it will be easier for you to do the work outside of the tree as you are going to be changing the API, which is not going to be easy to do in an incremental patch series.
And yes, this isn't going to be a trivial amount of work, sorry.
thanks,
greg k-h
On Wed, Oct 29, 2014 at 10:50:19AM +0800, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 03:05:16PM +0000, Qais Yousef wrote:
On 10/28/2014 02:13 PM, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 01:18:28PM +0000, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
So instead you created a one-off api just for this hardware? Ick, no, please work with the audio developers to incorporate it into the standard Linux audio apis so that everyone can benifit and not require special userspace programs to drive this hardware.
thanks,
greg k-h
OK. I'll wait to hear from alsa developers to see the extent of work required. I can't see it being trivial though. Would it be possible for this to be accepted into staging until this is resolved?
If you are willing to abide by the staging rules:
- incremental patches only doing one thing at a time
- never break the build
- constantly moving forward to getting merged (i.e. no new features being added)
I think it will be easier for you to do the work outside of the tree as you are going to be changing the API, which is not going to be easy to do in an incremental patch series.
And yes, this isn't going to be a trivial amount of work, sorry.
I am not sure if it can fit into staging model. The whole design of driver would need rework and all the infrastructure added here for exposing stuff to usermode will be thrown away, so this driver would simply need an major overhaul.
If this was ALSA driver and we would need to plumb it for acceptance then would have made sense in include, but thats not the fact today
Thanks
On 10/29/2014 05:24 AM, Vinod Koul wrote:
On Wed, Oct 29, 2014 at 10:50:19AM +0800, Greg Kroah-Hartman wrote:
cepted into staging until this is resolved? If you are willing to abide by the staging rules:
- incremental patches only doing one thing at a time
- never break the build
- constantly moving forward to getting merged (i.e. no new features being added)
I think it will be easier for you to do the work outside of the tree as you are going to be changing the API, which is not going to be easy to do in an incremental patch series.
And yes, this isn't going to be a trivial amount of work, sorry.
I am not sure if it can fit into staging model. The whole design of driver would need rework and all the infrastructure added here for exposing stuff to usermode will be thrown away, so this driver would simply need an major overhaul.
If this was ALSA driver and we would need to plumb it for acceptance then would have made sense in include, but thats not the fact today
Thanks
Thanks for all the comments Vinod. Looks like I made a bad judgement, clearly I wouldn't want to rewrite the code twice :) I'll take a closer look at using the framework and maybe start with the features that we know will work and build up the rest incrementally and address problems from there.
Thanks, Qais
On Tue, Oct 28, 2014 at 10:13:48PM +0800, Greg Kroah-Hartman wrote:
On Tue, Oct 28, 2014 at 01:18:28PM +0000, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
So instead you created a one-off api just for this hardware? Ick, no, please work with the audio developers to incorporate it into the standard Linux audio apis so that everyone can benifit and not require special userspace programs to drive this hardware.
I think it more a case of I want it do this by method A, ...naaaaah thats not what is availble in ALSA so let me redo the whole stack rather than model my driver to use ALSA
On 10/28/2014 02:18 PM, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
[...]
This doesn't sound to different from any of the other supported audio DSPs. ALSA seems to have 95% of what you need. And the missing 5% is probably stuff that is not specific to your hardware but rather something that other hardware will need as well. The framework is not set in stone you can make modifications and add the features that are missing to make your hardware work.
E.g. look at sound/soc/intel/ for an example of a audio DSP.
No integrating this into ALSA will quite likely result in a quite messy situation for you on the long run.
- Lars
On 10/28/2014 02:54 PM, Lars-Peter Clausen wrote:
On 10/28/2014 02:18 PM, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
[...]
This doesn't sound to different from any of the other supported audio DSPs. ALSA seems to have 95% of what you need. And the missing 5% is probably stuff that is not specific to your hardware but rather something that other hardware will need as well. The framework is not set in stone you can make modifications and add the features that are missing to make your hardware work.
E.g. look at sound/soc/intel/ for an example of a audio DSP.
No integrating this into ALSA will quite likely result in a quite messy situation for you on the long run.
- Lars
OK thanks for the reference. To be honest it felt like 15-20% of the features are missing but I'll need to look at the specifics and judge/estimate better.
Thanks, Qais
On 10/28/2014 04:33 PM, Qais Yousef wrote:
On 10/28/2014 02:54 PM, Lars-Peter Clausen wrote:
On 10/28/2014 02:18 PM, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
[...]
This doesn't sound to different from any of the other supported audio DSPs. ALSA seems to have 95% of what you need. And the missing 5% is probably stuff that is not specific to your hardware but rather something that other hardware will need as well. The framework is not set in stone you can make modifications and add the features that are missing to make your hardware work.
E.g. look at sound/soc/intel/ for an example of a audio DSP.
No integrating this into ALSA will quite likely result in a quite messy situation for you on the long run.
- Lars
OK thanks for the reference. To be honest it felt like 15-20% of the features are missing but I'll need to look at the specifics and judge/estimate better.
Even then you'd want ALSA integration. You (or your customers) will eventually want to hook some external devices up to the I2S or SPDIF ports. All the drivers for the external devices are implemented in ALSA/ASoC.
- Lars
On Tue, Oct 28, 2014 at 01:18:28PM +0000, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for
FWIW ALSA does not support decoders. The compressed ALSA API allows you to encode or decode your conent on a DSP, it provides API for that
If you have an additional need for a decoder or an encoder which is not listed, feel free to send a patch! The most important point is frameowrk is there for thsi so you need to use that and add whatever is not supported
taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
I disagree. You can use alsa PCM device to send pcm data and then use alsa compressed device to take back the compressed content. The two devices need to be "connected" using DPCM and DAPM frameowork availble. It should work with little or no changes
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
- Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
And why can't you use ALSA kcontrols for volume??
- Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Again why can't you use ALSA kcontrol for effects, be it PCM or compressed progarmming an EQ thru alsa kcontrol remains same
- Encoding/decoding acceleration is not supported as mentioned above.
Wrong on this count
It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
Yes and it should work
I added it to drivers/char as this is the best location I could think of at the moment.
drivers/misc?
Yeah could do if there's consensus.
NAK for that, this should be an ALSA driver
On 10/28/14, 8:18 AM, Qais Yousef wrote:
On 10/28/2014 11:55 AM, Clemens Ladisch wrote:
Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback.
What exactly do you mean with "synchronisation" and "playback"?
Synchronisation refers to accurate audio playout relative to a master clock source including compensation of drift between the master clock source and the playout clock of the audio hardware. Hence allowing synchronised audio playout across multiple independent devices.
Playback simple refers to the fact that AXD is capable of managing audio playout hardware like I2S and SPDIF interfaces.
It doesn't fit in alsa subsystem but I Cced them to confirm.
... because those two words sound like something that a sound card could do.
The problem mainly stems from the fact that we take a variety of compressed audio as input and we could perform audio encoding. The problem with the compressed audio is that the range of decoders and configuration supported in alsa is limited and there's no support for taking raw pcm and producing compressed output. I'm not an expert on alsa but when I looked it looked like there's more infra structure required.
The following not supported points from Documentation/sound/alsa/compress_offload.txt affect us:
Volume control/routing is not handled by this API. Devices exposing a compressed data interface will be considered as regular ALSA devices; volume changes and routing information will be provided with regular ALSA kcontrols.
Embedded audio effects. Such effects should be enabled in the same manner, no matter if the input was PCM or compressed.
Encoding/decoding acceleration is not supported as mentioned above. It is possible to route the output of a decoder to a capture stream, or even implement transcoding capabilities. This routing would be enabled with ALSA kcontrols.
Volume control, effects and routing aren't supported directly with the snd_pcm streaming interface either. we didn't add these features either in the compressed API since they could be enabled through existing means (kcontrols and routing).
I added it to drivers/char as this is the best location I could think of at the moment.
drivers/misc?
Yeah could do if there's consensus.
Thanks, Qais
Regards, Clemens
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On 10/28/2014 12:26 PM, Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback. This series adds the driver and DT binding document. [...] I added it to drivers/char as this is the best location I could think of at the moment. It doesn't fit in alsa subsystem but I Cced them to confirm. [...]
I think it will fit in nicely.
- Lars
On 10/28/2014 12:06 PM, Lars-Peter Clausen wrote:
On 10/28/2014 12:26 PM, Qais Yousef wrote:
AXD Audio Processing IP performs audio decoding, encoding, mixing, equalisation, synchronisation and playback. This series adds the driver and DT binding document. [...] I added it to drivers/char as this is the best location I could think of at the moment. It doesn't fit in alsa subsystem but I Cced them to confirm. [...]
I think it will fit in nicely.
- Lars
Check my answer to Clemens.
Thanks, Qais
participants (6)
-
Clemens Ladisch
-
Greg Kroah-Hartman
-
Lars-Peter Clausen
-
Pierre-Louis Bossart
-
Qais Yousef
-
Vinod Koul