#ifndef _BT_AUDIO_H
#define _BT_AUDIO_H

/* Version number */
#define SCSC_BT_AUDIO_ABOX_VERSION_MAJOR		(0x01)
#define SCSC_BT_AUDIO_ABOX_VERSION_MINOR		(0x01)

/* The A-Box uses a ARM Cortex-A7 with a 64 bytes cache line whereas
 * the WLBT uses a ARM Cortex-R4 with a 32 bytes cache line. The data
 * needs to be aligned to the largest cache line
 */
#define SCSC_BT_AUDIO_ABOX_DCACHE_LINE_WIDTH		(64)

/* kernel page size used for memory alignment */
#define SCSC_BT_AUDIO_PAGE_SIZE				(PAGE_SIZE)

/* Total size of the shared memory in one direction */
#define SCSC_BT_AUDIO_ABOX_DATA_SIZE			(128 * SCSC_BT_AUDIO_ABOX_DCACHE_LINE_WIDTH)

/* Size of the buffer for each interface */
#define SCSC_BT_AUDIO_ABOX_IF_0_SIZE			(10 * SCSC_BT_AUDIO_ABOX_DCACHE_LINE_WIDTH)
#define SCSC_BT_AUDIO_ABOX_IF_1_SIZE			(10 * SCSC_BT_AUDIO_ABOX_DCACHE_LINE_WIDTH)

/* Feature mask */
#define SCSC_BT_AUDIO_FEATURE_STREAMING_IF_0		(0x00000001)
#define SCSC_BT_AUDIO_FEATURE_STREAMING_IF_1		(0x00000002)
#define SCSC_BT_AUDIO_FEATURE_MESSAGING			(0x00000004)
#define SCSC_BT_AUDIO_FEATURE_A2DP_OFFLOAD		(0x00000008)

struct scsc_bt_audio_abox {
	/* AP RW - BT R4 RO - ABOX RO - 128 octets */

	/* header */
	uint32_t magic_value;
	uint16_t version_major;
	uint16_t version_minor;

	/* align to cache line (32 bytes) */
	uint8_t  reserved1[0x18];

	/* streaming interface 0 */
	uint32_t abox_to_bt_streaming_if_0_size;

	/* offset in abox_to_bt_streaming_if_data */
	uint32_t abox_to_bt_streaming_if_0_offset;

	uint32_t bt_to_abox_streaming_if_0_size;

	/* offset in bt_to_abox_streaming_if_data */
	uint32_t bt_to_abox_streaming_if_0_offset;

	/* streaming interface 1 */
	uint32_t abox_to_bt_streaming_if_1_size;

	/* offset in abox_to_bt_streaming_if_data */
	uint32_t abox_to_bt_streaming_if_1_offset;

	uint32_t bt_to_abox_streaming_if_1_size;

	/* offset in bt_to_abox_streaming_if_data */
	uint32_t bt_to_abox_streaming_if_1_offset;

	/* reserved room for additional AP information (64 bytes) */
	uint8_t  reserved2[0x40];

	/* AP RO - BT R4 RO - ABOX RW - 64 octets */
	uint32_t abox_fw_features;

	/* BTWLAN audio and ABOX firmware may start at different time
	 * and a number of interrupts may be already triggered by ABOX
	 * firmware before BTWLAN audio can process them causing
	 * misalignment between the two systems (e.g. both accessing
	 * the same buffer at the same time). The fields below provide
	 * information about which half of the double buffer the ABOX
	 * firmware is processing using 0/1.
	 * filled by ABOX firmware at each interrupt (read/write) and
	 * initialised to 0 by BT driver.
	 */
	uint32_t bt_to_abox_streaming_if_0_current_index;
	uint32_t abox_to_bt_streaming_if_0_current_index;
	uint32_t bt_to_abox_streaming_if_1_current_index;
	uint32_t abox_to_bt_streaming_if_1_current_index;

	/* align to cache line (64 bytes) */
	uint8_t  reserved3[0x2C];

	/* AP RO - BT R4 RW - ABOX RO - 64 octets */
	uint32_t bt_fw_features;

	/* sample rate (Hz) of the streaming interfaces */
	uint32_t streaming_if_0_sample_rate;
	uint32_t streaming_if_1_sample_rate;

	uint8_t  reserved4[0x34];

	/* payload */

	/* AP RO - BT R4 RO - ABOX RW - multiple of 64 octets */
	uint8_t  abox_to_bt_streaming_if_data[SCSC_BT_AUDIO_ABOX_DATA_SIZE];

	/* AP RO - BT R4 RW - ABOX RO - multiple of 64 octets */
	uint8_t  bt_to_abox_streaming_if_data[SCSC_BT_AUDIO_ABOX_DATA_SIZE];
};

/* Magic value */
#define SCSC_BT_AUDIO_ABOX_MAGIC_VALUE \
	(((offsetof(struct scsc_bt_audio_abox, abox_to_bt_streaming_if_0_size) << 20) | \
	(offsetof(struct scsc_bt_audio_abox, bt_to_abox_streaming_if_0_current_index) << 10) | \
	offsetof(struct scsc_bt_audio_abox, abox_to_bt_streaming_if_data)) ^ \
	0xBA12EF82)

#ifndef CONFIG_SOC_EXYNOS7885
struct scsc_bt_audio {
	struct device			*dev;
	struct scsc_bt_audio_abox	*abox_virtual;
	struct scsc_bt_audio_abox	*abox_physical;
	int (*dev_iommu_map)(struct device *, phys_addr_t, size_t);
	void (*dev_iommu_unmap)(struct device *, size_t);
};

struct scsc_bt_audio_driver {
	const char *name;
	void (*probe)(struct scsc_bt_audio_driver *driver, struct scsc_bt_audio *bt_audio);
	void (*remove)(struct scsc_bt_audio *bt_audio);
};

phys_addr_t scsc_bt_audio_get_paddr_buf(bool tx);
unsigned int scsc_bt_audio_get_rate(int id);
int scsc_bt_audio_register(struct device *dev,
		int (*dev_iommu_map)(struct device *, phys_addr_t, size_t),
		void (*dev_iommu_unmap)(struct device *, size_t));
int scsc_bt_audio_unregister(struct device *dev);
#else
struct scsc_bt_audio {
	struct device			*dev;
	struct scsc_bt_audio_abox	*abox_virtual;
	struct scsc_bt_audio_abox	*abox_physical;
};

struct scsc_bt_audio_driver {
	const char *name;
	void (*probe)(struct scsc_bt_audio_driver *driver, struct scsc_bt_audio *bt_audio);
	void (*remove)(struct scsc_bt_audio *bt_audio);
};

int scsc_bt_audio_register(struct scsc_bt_audio_driver *driver);
int scsc_bt_audio_unregister(struct scsc_bt_audio_driver *driver);
#endif

#endif /* _BT_AUDIO_H */