xref: /linux/sound/pci/lx6464es/lx_core.c (revision 2f27fce67173bbb05d5a0ee03dae5c021202c912)
177f5075aSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
202bec490STim Blechmann /* -*- linux-c -*- *
302bec490STim Blechmann  *
402bec490STim Blechmann  * ALSA driver for the digigram lx6464es interface
502bec490STim Blechmann  * low-level interface
602bec490STim Blechmann  *
702bec490STim Blechmann  * Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
802bec490STim Blechmann  */
902bec490STim Blechmann 
1002bec490STim Blechmann /* #define RMH_DEBUG 1 */
1102bec490STim Blechmann 
12c546ca95SMaxime Ripard #include <linux/bitops.h>
1302bec490STim Blechmann #include <linux/module.h>
1402bec490STim Blechmann #include <linux/pci.h>
1502bec490STim Blechmann #include <linux/delay.h>
1602bec490STim Blechmann 
1702bec490STim Blechmann #include "lx6464es.h"
1802bec490STim Blechmann #include "lx_core.h"
1902bec490STim Blechmann 
2002bec490STim Blechmann /* low-level register access */
2102bec490STim Blechmann 
2202bec490STim Blechmann static const unsigned long dsp_port_offsets[] = {
2302bec490STim Blechmann 	0,
2402bec490STim Blechmann 	0x400,
2502bec490STim Blechmann 	0x401,
2602bec490STim Blechmann 	0x402,
2702bec490STim Blechmann 	0x403,
2802bec490STim Blechmann 	0x404,
2902bec490STim Blechmann 	0x405,
3002bec490STim Blechmann 	0x406,
3102bec490STim Blechmann 	0x407,
3202bec490STim Blechmann 	0x408,
3302bec490STim Blechmann 	0x409,
3402bec490STim Blechmann 	0x40a,
3502bec490STim Blechmann 	0x40b,
3602bec490STim Blechmann 	0x40c,
3702bec490STim Blechmann 
3802bec490STim Blechmann 	0x410,
3902bec490STim Blechmann 	0x411,
4002bec490STim Blechmann 	0x412,
4102bec490STim Blechmann 	0x413,
4202bec490STim Blechmann 	0x414,
4302bec490STim Blechmann 	0x415,
4402bec490STim Blechmann 	0x416,
4502bec490STim Blechmann 
4602bec490STim Blechmann 	0x420,
4702bec490STim Blechmann 	0x430,
4802bec490STim Blechmann 	0x431,
4902bec490STim Blechmann 	0x432,
5002bec490STim Blechmann 	0x433,
5102bec490STim Blechmann 	0x434,
5202bec490STim Blechmann 	0x440
5302bec490STim Blechmann };
5402bec490STim Blechmann 
5502bec490STim Blechmann static void __iomem *lx_dsp_register(struct lx6464es *chip, int port)
5602bec490STim Blechmann {
5702bec490STim Blechmann 	void __iomem *base_address = chip->port_dsp_bar;
5802bec490STim Blechmann 	return base_address + dsp_port_offsets[port]*4;
5902bec490STim Blechmann }
6002bec490STim Blechmann 
6102bec490STim Blechmann unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port)
6202bec490STim Blechmann {
6302bec490STim Blechmann 	void __iomem *address = lx_dsp_register(chip, port);
6402bec490STim Blechmann 	return ioread32(address);
6502bec490STim Blechmann }
6602bec490STim Blechmann 
67afd00d72STim Blechmann static void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data,
68afd00d72STim Blechmann 			       u32 len)
6902bec490STim Blechmann {
70a2987855STim Blechmann 	u32 __iomem *address = lx_dsp_register(chip, port);
71a2987855STim Blechmann 	int i;
72a2987855STim Blechmann 
73a2987855STim Blechmann 	/* we cannot use memcpy_fromio */
74a2987855STim Blechmann 	for (i = 0; i != len; ++i)
75a2987855STim Blechmann 		data[i] = ioread32(address + i);
7602bec490STim Blechmann }
7702bec490STim Blechmann 
7802bec490STim Blechmann 
7902bec490STim Blechmann void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data)
8002bec490STim Blechmann {
8102bec490STim Blechmann 	void __iomem *address = lx_dsp_register(chip, port);
8202bec490STim Blechmann 	iowrite32(data, address);
8302bec490STim Blechmann }
8402bec490STim Blechmann 
85afd00d72STim Blechmann static void lx_dsp_reg_writebuf(struct lx6464es *chip, int port,
86afd00d72STim Blechmann 				const u32 *data, u32 len)
8702bec490STim Blechmann {
88a2987855STim Blechmann 	u32 __iomem *address = lx_dsp_register(chip, port);
89a2987855STim Blechmann 	int i;
90a2987855STim Blechmann 
91a2987855STim Blechmann 	/* we cannot use memcpy_to */
92a2987855STim Blechmann 	for (i = 0; i != len; ++i)
93a2987855STim Blechmann 		iowrite32(data[i], address + i);
9402bec490STim Blechmann }
9502bec490STim Blechmann 
9602bec490STim Blechmann 
9702bec490STim Blechmann static const unsigned long plx_port_offsets[] = {
9802bec490STim Blechmann 	0x04,
9902bec490STim Blechmann 	0x40,
10002bec490STim Blechmann 	0x44,
10102bec490STim Blechmann 	0x48,
10202bec490STim Blechmann 	0x4c,
10302bec490STim Blechmann 	0x50,
10402bec490STim Blechmann 	0x54,
10502bec490STim Blechmann 	0x58,
10602bec490STim Blechmann 	0x5c,
10702bec490STim Blechmann 	0x64,
10802bec490STim Blechmann 	0x68,
10902bec490STim Blechmann 	0x6C
11002bec490STim Blechmann };
11102bec490STim Blechmann 
11202bec490STim Blechmann static void __iomem *lx_plx_register(struct lx6464es *chip, int port)
11302bec490STim Blechmann {
11402bec490STim Blechmann 	void __iomem *base_address = chip->port_plx_remapped;
11502bec490STim Blechmann 	return base_address + plx_port_offsets[port];
11602bec490STim Blechmann }
11702bec490STim Blechmann 
11802bec490STim Blechmann unsigned long lx_plx_reg_read(struct lx6464es *chip, int port)
11902bec490STim Blechmann {
12002bec490STim Blechmann 	void __iomem *address = lx_plx_register(chip, port);
12102bec490STim Blechmann 	return ioread32(address);
12202bec490STim Blechmann }
12302bec490STim Blechmann 
12402bec490STim Blechmann void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data)
12502bec490STim Blechmann {
12602bec490STim Blechmann 	void __iomem *address = lx_plx_register(chip, port);
12702bec490STim Blechmann 	iowrite32(data, address);
12802bec490STim Blechmann }
12902bec490STim Blechmann 
13002bec490STim Blechmann /* rmh */
13102bec490STim Blechmann 
13202bec490STim Blechmann #ifdef CONFIG_SND_DEBUG
13302bec490STim Blechmann #define CMD_NAME(a) a
13402bec490STim Blechmann #else
13502bec490STim Blechmann #define CMD_NAME(a) NULL
13602bec490STim Blechmann #endif
13702bec490STim Blechmann 
13802bec490STim Blechmann #define Reg_CSM_MR			0x00000002
13902bec490STim Blechmann #define Reg_CSM_MC			0x00000001
14002bec490STim Blechmann 
14102bec490STim Blechmann struct dsp_cmd_info {
14202bec490STim Blechmann 	u32    dcCodeOp;	/* Op Code of the command (usually 1st 24-bits
14302bec490STim Blechmann 				 * word).*/
14402bec490STim Blechmann 	u16    dcCmdLength;	/* Command length in words of 24 bits.*/
14502bec490STim Blechmann 	u16    dcStatusType;	/* Status type: 0 for fixed length, 1 for
14602bec490STim Blechmann 				 * random. */
14702bec490STim Blechmann 	u16    dcStatusLength;	/* Status length (if fixed).*/
14802bec490STim Blechmann 	char  *dcOpName;
14902bec490STim Blechmann };
15002bec490STim Blechmann 
15102bec490STim Blechmann /*
15202bec490STim Blechmann   Initialization and control data for the Microblaze interface
15302bec490STim Blechmann   - OpCode:
15402bec490STim Blechmann     the opcode field of the command set at the proper offset
15502bec490STim Blechmann   - CmdLength
15602bec490STim Blechmann     the number of command words
15702bec490STim Blechmann   - StatusType
15802bec490STim Blechmann     offset in the status registers: 0 means that the return value may be
15902bec490STim Blechmann     different from 0, and must be read
16002bec490STim Blechmann   - StatusLength
16102bec490STim Blechmann     the number of status words (in addition to the return value)
16202bec490STim Blechmann */
16302bec490STim Blechmann 
1642f200ce2STakashi Iwai static const struct dsp_cmd_info dsp_commands[] =
16502bec490STim Blechmann {
16602bec490STim Blechmann 	{ (CMD_00_INFO_DEBUG << OPCODE_OFFSET)			, 1 /*custom*/
16702bec490STim Blechmann 	  , 1	, 0 /**/		    , CMD_NAME("INFO_DEBUG") },
16802bec490STim Blechmann 	{ (CMD_01_GET_SYS_CFG << OPCODE_OFFSET) 		, 1 /**/
16902bec490STim Blechmann 	  , 1      , 2 /**/		    , CMD_NAME("GET_SYS_CFG") },
17002bec490STim Blechmann 	{ (CMD_02_SET_GRANULARITY << OPCODE_OFFSET)	        , 1 /**/
17102bec490STim Blechmann 	  , 1      , 0 /**/		    , CMD_NAME("SET_GRANULARITY") },
17202bec490STim Blechmann 	{ (CMD_03_SET_TIMER_IRQ << OPCODE_OFFSET)		, 1 /**/
17302bec490STim Blechmann 	  , 1      , 0 /**/		    , CMD_NAME("SET_TIMER_IRQ") },
17402bec490STim Blechmann 	{ (CMD_04_GET_EVENT << OPCODE_OFFSET)			, 1 /**/
17502bec490STim Blechmann 	  , 1      , 0 /*up to 10*/     , CMD_NAME("GET_EVENT") },
17602bec490STim Blechmann 	{ (CMD_05_GET_PIPES << OPCODE_OFFSET)			, 1 /**/
17702bec490STim Blechmann 	  , 1      , 2 /*up to 4*/      , CMD_NAME("GET_PIPES") },
17802bec490STim Blechmann 	{ (CMD_06_ALLOCATE_PIPE << OPCODE_OFFSET)		, 1 /**/
17902bec490STim Blechmann 	  , 0      , 0 /**/		    , CMD_NAME("ALLOCATE_PIPE") },
18002bec490STim Blechmann 	{ (CMD_07_RELEASE_PIPE << OPCODE_OFFSET)		, 1 /**/
18102bec490STim Blechmann 	  , 0      , 0 /**/		    , CMD_NAME("RELEASE_PIPE") },
18202bec490STim Blechmann 	{ (CMD_08_ASK_BUFFERS << OPCODE_OFFSET) 		, 1 /**/
18302bec490STim Blechmann 	  , 1      , MAX_STREAM_BUFFER  , CMD_NAME("ASK_BUFFERS") },
18402bec490STim Blechmann 	{ (CMD_09_STOP_PIPE << OPCODE_OFFSET)			, 1 /**/
18502bec490STim Blechmann 	  , 0      , 0 /*up to 2*/      , CMD_NAME("STOP_PIPE") },
18602bec490STim Blechmann 	{ (CMD_0A_GET_PIPE_SPL_COUNT << OPCODE_OFFSET)	        , 1 /**/
18702bec490STim Blechmann 	  , 1      , 1 /*up to 2*/      , CMD_NAME("GET_PIPE_SPL_COUNT") },
18802bec490STim Blechmann 	{ (CMD_0B_TOGGLE_PIPE_STATE << OPCODE_OFFSET)           , 1 /*up to 5*/
18902bec490STim Blechmann 	  , 1      , 0 /**/		    , CMD_NAME("TOGGLE_PIPE_STATE") },
19002bec490STim Blechmann 	{ (CMD_0C_DEF_STREAM << OPCODE_OFFSET)			, 1 /*up to 4*/
19102bec490STim Blechmann 	  , 1      , 0 /**/		    , CMD_NAME("DEF_STREAM") },
19202bec490STim Blechmann 	{ (CMD_0D_SET_MUTE  << OPCODE_OFFSET)			, 3 /**/
19302bec490STim Blechmann 	  , 1      , 0 /**/		    , CMD_NAME("SET_MUTE") },
19402bec490STim Blechmann 	{ (CMD_0E_GET_STREAM_SPL_COUNT << OPCODE_OFFSET)        , 1/**/
19502bec490STim Blechmann 	  , 1      , 2 /**/		    , CMD_NAME("GET_STREAM_SPL_COUNT") },
19602bec490STim Blechmann 	{ (CMD_0F_UPDATE_BUFFER << OPCODE_OFFSET)		, 3 /*up to 4*/
19702bec490STim Blechmann 	  , 0      , 1 /**/		    , CMD_NAME("UPDATE_BUFFER") },
19802bec490STim Blechmann 	{ (CMD_10_GET_BUFFER << OPCODE_OFFSET)			, 1 /**/
19902bec490STim Blechmann 	  , 1      , 4 /**/		    , CMD_NAME("GET_BUFFER") },
20002bec490STim Blechmann 	{ (CMD_11_CANCEL_BUFFER << OPCODE_OFFSET)		, 1 /**/
20102bec490STim Blechmann 	  , 1      , 1 /*up to 4*/      , CMD_NAME("CANCEL_BUFFER") },
20202bec490STim Blechmann 	{ (CMD_12_GET_PEAK << OPCODE_OFFSET)			, 1 /**/
20302bec490STim Blechmann 	  , 1      , 1 /**/		    , CMD_NAME("GET_PEAK") },
20402bec490STim Blechmann 	{ (CMD_13_SET_STREAM_STATE << OPCODE_OFFSET)	        , 1 /**/
20502bec490STim Blechmann 	  , 1      , 0 /**/		    , CMD_NAME("SET_STREAM_STATE") },
20602bec490STim Blechmann };
20702bec490STim Blechmann 
20802bec490STim Blechmann static void lx_message_init(struct lx_rmh *rmh, enum cmd_mb_opcodes cmd)
20902bec490STim Blechmann {
21002bec490STim Blechmann 	snd_BUG_ON(cmd >= CMD_14_INVALID);
21102bec490STim Blechmann 
21202bec490STim Blechmann 	rmh->cmd[0] = dsp_commands[cmd].dcCodeOp;
21302bec490STim Blechmann 	rmh->cmd_len = dsp_commands[cmd].dcCmdLength;
21402bec490STim Blechmann 	rmh->stat_len = dsp_commands[cmd].dcStatusLength;
21502bec490STim Blechmann 	rmh->dsp_stat = dsp_commands[cmd].dcStatusType;
21602bec490STim Blechmann 	rmh->cmd_idx = cmd;
21702bec490STim Blechmann 	memset(&rmh->cmd[1], 0, (REG_CRM_NUMBER - 1) * sizeof(u32));
21802bec490STim Blechmann 
21902bec490STim Blechmann #ifdef CONFIG_SND_DEBUG
22002bec490STim Blechmann 	memset(rmh->stat, 0, REG_CRM_NUMBER * sizeof(u32));
22102bec490STim Blechmann #endif
22202bec490STim Blechmann #ifdef RMH_DEBUG
22302bec490STim Blechmann 	rmh->cmd_idx = cmd;
22402bec490STim Blechmann #endif
22502bec490STim Blechmann }
22602bec490STim Blechmann 
22702bec490STim Blechmann #ifdef RMH_DEBUG
22802bec490STim Blechmann #define LXRMH "lx6464es rmh: "
22902bec490STim Blechmann static void lx_message_dump(struct lx_rmh *rmh)
23002bec490STim Blechmann {
23102bec490STim Blechmann 	u8 idx = rmh->cmd_idx;
23202bec490STim Blechmann 	int i;
23302bec490STim Blechmann 
234*8455587cSTakashi Iwai 	pr_debug(LXRMH "command %s\n", dsp_commands[idx].dcOpName);
23502bec490STim Blechmann 
23602bec490STim Blechmann 	for (i = 0; i != rmh->cmd_len; ++i)
237*8455587cSTakashi Iwai 		pr_debug(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]);
23802bec490STim Blechmann 
23902bec490STim Blechmann 	for (i = 0; i != rmh->stat_len; ++i)
240*8455587cSTakashi Iwai 		pr_debug(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]);
241*8455587cSTakashi Iwai 	pr_debug("\n");
24202bec490STim Blechmann }
24302bec490STim Blechmann #else
24402bec490STim Blechmann static inline void lx_message_dump(struct lx_rmh *rmh)
24502bec490STim Blechmann {}
24602bec490STim Blechmann #endif
24702bec490STim Blechmann 
24802bec490STim Blechmann 
24902bec490STim Blechmann 
25002bec490STim Blechmann /* sleep 500 - 100 = 400 times 100us -> the timeout is >= 40 ms */
25102bec490STim Blechmann #define XILINX_TIMEOUT_MS       40
25202bec490STim Blechmann #define XILINX_POLL_NO_SLEEP    100
25302bec490STim Blechmann #define XILINX_POLL_ITERATIONS  150
25402bec490STim Blechmann 
25502bec490STim Blechmann 
25602bec490STim Blechmann static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh)
25702bec490STim Blechmann {
25802bec490STim Blechmann 	u32 reg = ED_DSP_TIMED_OUT;
25902bec490STim Blechmann 	int dwloop;
26002bec490STim Blechmann 
26102bec490STim Blechmann 	if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) {
262be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "PIOSendMessage eReg_CSM %x\n", reg);
26302bec490STim Blechmann 		return -EBUSY;
26402bec490STim Blechmann 	}
26502bec490STim Blechmann 
26602bec490STim Blechmann 	/* write command */
26702bec490STim Blechmann 	lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len);
26802bec490STim Blechmann 
26902bec490STim Blechmann 	/* MicoBlaze gogogo */
27002bec490STim Blechmann 	lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC);
27102bec490STim Blechmann 
27295eff499STim Blechmann 	/* wait for device to answer */
27302bec490STim Blechmann 	for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS * 1000; ++dwloop) {
27402bec490STim Blechmann 		if (lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR) {
27502bec490STim Blechmann 			if (rmh->dsp_stat == 0)
27602bec490STim Blechmann 				reg = lx_dsp_reg_read(chip, eReg_CRM1);
27702bec490STim Blechmann 			else
27802bec490STim Blechmann 				reg = 0;
27902bec490STim Blechmann 			goto polling_successful;
28002bec490STim Blechmann 		} else
28102bec490STim Blechmann 			udelay(1);
28202bec490STim Blechmann 	}
283be4e6d3cSTakashi Iwai 	dev_warn(chip->card->dev, "TIMEOUT lx_message_send_atomic! "
28402bec490STim Blechmann 		   "polling failed\n");
28502bec490STim Blechmann 
28602bec490STim Blechmann polling_successful:
28702bec490STim Blechmann 	if ((reg & ERROR_VALUE) == 0) {
28802bec490STim Blechmann 		/* read response */
28902bec490STim Blechmann 		if (rmh->stat_len) {
29002bec490STim Blechmann 			snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1));
29102bec490STim Blechmann 			lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat,
29202bec490STim Blechmann 					   rmh->stat_len);
29302bec490STim Blechmann 		}
29402bec490STim Blechmann 	} else
295be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "rmh error: %08x\n", reg);
29602bec490STim Blechmann 
29702bec490STim Blechmann 	/* clear Reg_CSM_MR */
29802bec490STim Blechmann 	lx_dsp_reg_write(chip, eReg_CSM, 0);
29902bec490STim Blechmann 
30002bec490STim Blechmann 	switch (reg) {
30102bec490STim Blechmann 	case ED_DSP_TIMED_OUT:
302be4e6d3cSTakashi Iwai 		dev_warn(chip->card->dev, "lx_message_send: dsp timeout\n");
30302bec490STim Blechmann 		return -ETIMEDOUT;
30402bec490STim Blechmann 
30502bec490STim Blechmann 	case ED_DSP_CRASHED:
306be4e6d3cSTakashi Iwai 		dev_warn(chip->card->dev, "lx_message_send: dsp crashed\n");
30702bec490STim Blechmann 		return -EAGAIN;
30802bec490STim Blechmann 	}
30902bec490STim Blechmann 
31002bec490STim Blechmann 	lx_message_dump(rmh);
31102bec490STim Blechmann 
31202bec490STim Blechmann 	return reg;
31302bec490STim Blechmann }
31402bec490STim Blechmann 
31502bec490STim Blechmann 
31602bec490STim Blechmann /* low-level dsp access */
317e23e7a14SBill Pemberton int lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
31802bec490STim Blechmann {
31902bec490STim Blechmann 	u16 ret;
32002bec490STim Blechmann 
3216336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
32202bec490STim Blechmann 
32302bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
32402bec490STim Blechmann 	ret = lx_message_send_atomic(chip, &chip->rmh);
32502bec490STim Blechmann 
32602bec490STim Blechmann 	*rdsp_version = chip->rmh.stat[1];
3276336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
32802bec490STim Blechmann 	return ret;
32902bec490STim Blechmann }
33002bec490STim Blechmann 
33102bec490STim Blechmann int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
33202bec490STim Blechmann {
33302bec490STim Blechmann 	u16 ret = 0;
33402bec490STim Blechmann 	u32 freq_raw = 0;
33502bec490STim Blechmann 	u32 freq = 0;
33602bec490STim Blechmann 	u32 frequency = 0;
33702bec490STim Blechmann 
3386336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
33902bec490STim Blechmann 
34002bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
34102bec490STim Blechmann 	ret = lx_message_send_atomic(chip, &chip->rmh);
34202bec490STim Blechmann 
34302bec490STim Blechmann 	if (ret == 0) {
34402bec490STim Blechmann 		freq_raw = chip->rmh.stat[0] >> FREQ_FIELD_OFFSET;
34502bec490STim Blechmann 		freq = freq_raw & XES_FREQ_COUNT8_MASK;
34602bec490STim Blechmann 
34702bec490STim Blechmann 		if ((freq < XES_FREQ_COUNT8_48_MAX) ||
34802bec490STim Blechmann 		    (freq > XES_FREQ_COUNT8_44_MIN))
34902bec490STim Blechmann 			frequency = 0; /* unknown */
35002bec490STim Blechmann 		else if (freq >= XES_FREQ_COUNT8_44_MAX)
35102bec490STim Blechmann 			frequency = 44100;
35202bec490STim Blechmann 		else
35302bec490STim Blechmann 			frequency = 48000;
35402bec490STim Blechmann 	}
35502bec490STim Blechmann 
3566336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
35702bec490STim Blechmann 
35802bec490STim Blechmann 	*rfreq = frequency * chip->freq_ratio;
35902bec490STim Blechmann 
36002bec490STim Blechmann 	return ret;
36102bec490STim Blechmann }
36202bec490STim Blechmann 
36380b52490STim Blechmann int lx_dsp_get_mac(struct lx6464es *chip)
36402bec490STim Blechmann {
36502bec490STim Blechmann 	u32 macmsb, maclsb;
36602bec490STim Blechmann 
36702bec490STim Blechmann 	macmsb = lx_dsp_reg_read(chip, eReg_ADMACESMSB) & 0x00FFFFFF;
36802bec490STim Blechmann 	maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF;
36902bec490STim Blechmann 
37002bec490STim Blechmann 	/* todo: endianess handling */
37180b52490STim Blechmann 	chip->mac_address[5] = ((u8 *)(&maclsb))[0];
37280b52490STim Blechmann 	chip->mac_address[4] = ((u8 *)(&maclsb))[1];
37380b52490STim Blechmann 	chip->mac_address[3] = ((u8 *)(&maclsb))[2];
37480b52490STim Blechmann 	chip->mac_address[2] = ((u8 *)(&macmsb))[0];
37580b52490STim Blechmann 	chip->mac_address[1] = ((u8 *)(&macmsb))[1];
37680b52490STim Blechmann 	chip->mac_address[0] = ((u8 *)(&macmsb))[2];
37702bec490STim Blechmann 
37802bec490STim Blechmann 	return 0;
37902bec490STim Blechmann }
38002bec490STim Blechmann 
38102bec490STim Blechmann 
38202bec490STim Blechmann int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran)
38302bec490STim Blechmann {
38402bec490STim Blechmann 	int ret;
38502bec490STim Blechmann 
3866336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
38702bec490STim Blechmann 
38802bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY);
38902bec490STim Blechmann 	chip->rmh.cmd[0] |= gran;
39002bec490STim Blechmann 
39102bec490STim Blechmann 	ret = lx_message_send_atomic(chip, &chip->rmh);
3926336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
39302bec490STim Blechmann 	return ret;
39402bec490STim Blechmann }
39502bec490STim Blechmann 
39602bec490STim Blechmann int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
39702bec490STim Blechmann {
39802bec490STim Blechmann 	int ret;
39902bec490STim Blechmann 
4006336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
40102bec490STim Blechmann 
40202bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_04_GET_EVENT);
40302bec490STim Blechmann 	chip->rmh.stat_len = 9;	/* we don't necessarily need the full length */
40402bec490STim Blechmann 
40502bec490STim Blechmann 	ret = lx_message_send_atomic(chip, &chip->rmh);
40602bec490STim Blechmann 
40702bec490STim Blechmann 	if (!ret)
40802bec490STim Blechmann 		memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32));
40902bec490STim Blechmann 
4106336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
41102bec490STim Blechmann 	return ret;
41202bec490STim Blechmann }
41302bec490STim Blechmann 
41402bec490STim Blechmann #define PIPE_INFO_TO_CMD(capture, pipe)					\
41502bec490STim Blechmann 	((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
41602bec490STim Blechmann 
41702bec490STim Blechmann 
41802bec490STim Blechmann 
41902bec490STim Blechmann /* low-level pipe handling */
42002bec490STim Blechmann int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
42102bec490STim Blechmann 		     int channels)
42202bec490STim Blechmann {
42302bec490STim Blechmann 	int err;
42402bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
42502bec490STim Blechmann 
4266336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
42702bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE);
42802bec490STim Blechmann 
42902bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
43002bec490STim Blechmann 	chip->rmh.cmd[0] |= channels;
43102bec490STim Blechmann 
43202bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
4336336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
43402bec490STim Blechmann 
43502bec490STim Blechmann 	if (err != 0)
436be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "could not allocate pipe\n");
43702bec490STim Blechmann 
43802bec490STim Blechmann 	return err;
43902bec490STim Blechmann }
44002bec490STim Blechmann 
44102bec490STim Blechmann int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture)
44202bec490STim Blechmann {
44302bec490STim Blechmann 	int err;
44402bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
44502bec490STim Blechmann 
4466336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
44702bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE);
44802bec490STim Blechmann 
44902bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
45002bec490STim Blechmann 
45102bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
4526336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
45302bec490STim Blechmann 
45402bec490STim Blechmann 	return err;
45502bec490STim Blechmann }
45602bec490STim Blechmann 
45702bec490STim Blechmann int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
45802bec490STim Blechmann 		  u32 *r_needed, u32 *r_freed, u32 *size_array)
45902bec490STim Blechmann {
46002bec490STim Blechmann 	int err;
46102bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
46202bec490STim Blechmann 
46302bec490STim Blechmann #ifdef CONFIG_SND_DEBUG
46402bec490STim Blechmann 	if (size_array)
46502bec490STim Blechmann 		memset(size_array, 0, sizeof(u32)*MAX_STREAM_BUFFER);
46602bec490STim Blechmann #endif
46702bec490STim Blechmann 
46802bec490STim Blechmann 	*r_needed = 0;
46902bec490STim Blechmann 	*r_freed = 0;
47002bec490STim Blechmann 
4716336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
47202bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS);
47302bec490STim Blechmann 
47402bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
47502bec490STim Blechmann 
47602bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
47702bec490STim Blechmann 
47802bec490STim Blechmann 	if (!err) {
47902bec490STim Blechmann 		int i;
48002bec490STim Blechmann 		for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
48102bec490STim Blechmann 			u32 stat = chip->rmh.stat[i];
48202bec490STim Blechmann 			if (stat & (BF_EOB << BUFF_FLAGS_OFFSET)) {
48302bec490STim Blechmann 				/* finished */
48402bec490STim Blechmann 				*r_freed += 1;
48502bec490STim Blechmann 				if (size_array)
48602bec490STim Blechmann 					size_array[i] = stat & MASK_DATA_SIZE;
48702bec490STim Blechmann 			} else if ((stat & (BF_VALID << BUFF_FLAGS_OFFSET))
48802bec490STim Blechmann 				   == 0)
48902bec490STim Blechmann 				/* free */
49002bec490STim Blechmann 				*r_needed += 1;
49102bec490STim Blechmann 		}
49202bec490STim Blechmann 
493be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev,
494be4e6d3cSTakashi Iwai 			"CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
49502bec490STim Blechmann 			    *r_needed, *r_freed);
4965dac9f8dSDan Carpenter 		for (i = 0; i < MAX_STREAM_BUFFER && i < chip->rmh.stat_len;
4975dac9f8dSDan Carpenter 		     ++i) {
4985dac9f8dSDan Carpenter 			dev_dbg(chip->card->dev, "  stat[%d]: %x, %x\n", i,
49902bec490STim Blechmann 				chip->rmh.stat[i],
50002bec490STim Blechmann 				chip->rmh.stat[i] & MASK_DATA_SIZE);
50102bec490STim Blechmann 		}
50202bec490STim Blechmann 	}
50302bec490STim Blechmann 
5046336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
50502bec490STim Blechmann 	return err;
50602bec490STim Blechmann }
50702bec490STim Blechmann 
50802bec490STim Blechmann 
50902bec490STim Blechmann int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture)
51002bec490STim Blechmann {
51102bec490STim Blechmann 	int err;
51202bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
51302bec490STim Blechmann 
5146336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
51502bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_09_STOP_PIPE);
51602bec490STim Blechmann 
51702bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
51802bec490STim Blechmann 
51902bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
52002bec490STim Blechmann 
5216336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
52202bec490STim Blechmann 	return err;
52302bec490STim Blechmann }
52402bec490STim Blechmann 
52502bec490STim Blechmann static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture)
52602bec490STim Blechmann {
52702bec490STim Blechmann 	int err;
52802bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
52902bec490STim Blechmann 
5306336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
53102bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE);
53202bec490STim Blechmann 
53302bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
53402bec490STim Blechmann 
53502bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
53602bec490STim Blechmann 
5376336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
53802bec490STim Blechmann 	return err;
53902bec490STim Blechmann }
54002bec490STim Blechmann 
54102bec490STim Blechmann 
54202bec490STim Blechmann int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture)
54302bec490STim Blechmann {
54402bec490STim Blechmann 	int err;
54502bec490STim Blechmann 
54602bec490STim Blechmann 	err = lx_pipe_wait_for_idle(chip, pipe, is_capture);
54702bec490STim Blechmann 	if (err < 0)
54802bec490STim Blechmann 		return err;
54902bec490STim Blechmann 
55002bec490STim Blechmann 	err = lx_pipe_toggle_state(chip, pipe, is_capture);
55102bec490STim Blechmann 
55202bec490STim Blechmann 	return err;
55302bec490STim Blechmann }
55402bec490STim Blechmann 
55502bec490STim Blechmann int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture)
55602bec490STim Blechmann {
55702bec490STim Blechmann 	int err = 0;
55802bec490STim Blechmann 
55902bec490STim Blechmann 	err = lx_pipe_wait_for_start(chip, pipe, is_capture);
56002bec490STim Blechmann 	if (err < 0)
56102bec490STim Blechmann 		return err;
56202bec490STim Blechmann 
56302bec490STim Blechmann 	err = lx_pipe_toggle_state(chip, pipe, is_capture);
56402bec490STim Blechmann 
56502bec490STim Blechmann 	return err;
56602bec490STim Blechmann }
56702bec490STim Blechmann 
56802bec490STim Blechmann 
56902bec490STim Blechmann int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
57002bec490STim Blechmann 			 u64 *rsample_count)
57102bec490STim Blechmann {
57202bec490STim Blechmann 	int err;
57302bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
57402bec490STim Blechmann 
5756336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
57602bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
57702bec490STim Blechmann 
57802bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
57902bec490STim Blechmann 	chip->rmh.stat_len = 2;	/* need all words here! */
58002bec490STim Blechmann 
58102bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh); /* don't sleep! */
58202bec490STim Blechmann 
58302bec490STim Blechmann 	if (err != 0)
584be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev,
585be4e6d3cSTakashi Iwai 			"could not query pipe's sample count\n");
58602bec490STim Blechmann 	else {
58702bec490STim Blechmann 		*rsample_count = ((u64)(chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
58802bec490STim Blechmann 				  << 24)     /* hi part */
58902bec490STim Blechmann 			+ chip->rmh.stat[1]; /* lo part */
59002bec490STim Blechmann 	}
59102bec490STim Blechmann 
5926336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
59302bec490STim Blechmann 	return err;
59402bec490STim Blechmann }
59502bec490STim Blechmann 
59602bec490STim Blechmann int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
59702bec490STim Blechmann {
59802bec490STim Blechmann 	int err;
59902bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
60002bec490STim Blechmann 
6016336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
60202bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
60302bec490STim Blechmann 
60402bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
60502bec490STim Blechmann 
60602bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
60702bec490STim Blechmann 
60802bec490STim Blechmann 	if (err != 0)
609be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "could not query pipe's state\n");
61002bec490STim Blechmann 	else
61102bec490STim Blechmann 		*rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F;
61202bec490STim Blechmann 
6136336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
61402bec490STim Blechmann 	return err;
61502bec490STim Blechmann }
61602bec490STim Blechmann 
61702bec490STim Blechmann static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
61802bec490STim Blechmann 				  int is_capture, u16 state)
61902bec490STim Blechmann {
62002bec490STim Blechmann 	int i;
62102bec490STim Blechmann 
62202bec490STim Blechmann 	/* max 2*PCMOnlyGranularity = 2*1024 at 44100 = < 50 ms:
62302bec490STim Blechmann 	 * timeout 50 ms */
62402bec490STim Blechmann 	for (i = 0; i != 50; ++i) {
62502bec490STim Blechmann 		u16 current_state;
62602bec490STim Blechmann 		int err = lx_pipe_state(chip, pipe, is_capture, &current_state);
62702bec490STim Blechmann 
62802bec490STim Blechmann 		if (err < 0)
62902bec490STim Blechmann 			return err;
63002bec490STim Blechmann 
631a19c921fSTakashi Iwai 		if (!err && current_state == state)
63202bec490STim Blechmann 			return 0;
63302bec490STim Blechmann 
63402bec490STim Blechmann 		mdelay(1);
63502bec490STim Blechmann 	}
63602bec490STim Blechmann 
63702bec490STim Blechmann 	return -ETIMEDOUT;
63802bec490STim Blechmann }
63902bec490STim Blechmann 
64002bec490STim Blechmann int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture)
64102bec490STim Blechmann {
64202bec490STim Blechmann 	return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_RUN);
64302bec490STim Blechmann }
64402bec490STim Blechmann 
64502bec490STim Blechmann int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture)
64602bec490STim Blechmann {
64702bec490STim Blechmann 	return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_IDLE);
64802bec490STim Blechmann }
64902bec490STim Blechmann 
65002bec490STim Blechmann /* low-level stream handling */
65102bec490STim Blechmann int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
65202bec490STim Blechmann 			       int is_capture, enum stream_state_t state)
65302bec490STim Blechmann {
65402bec490STim Blechmann 	int err;
65502bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
65602bec490STim Blechmann 
6576336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
65802bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE);
65902bec490STim Blechmann 
66002bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
66102bec490STim Blechmann 	chip->rmh.cmd[0] |= state;
66202bec490STim Blechmann 
66302bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
6646336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
66502bec490STim Blechmann 
66602bec490STim Blechmann 	return err;
66702bec490STim Blechmann }
66802bec490STim Blechmann 
66902bec490STim Blechmann int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
67002bec490STim Blechmann 			 u32 pipe, int is_capture)
67102bec490STim Blechmann {
67202bec490STim Blechmann 	int err;
67302bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
67402bec490STim Blechmann 	u32 channels = runtime->channels;
67502bec490STim Blechmann 
6766336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
67702bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM);
67802bec490STim Blechmann 
67902bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
68002bec490STim Blechmann 
68102bec490STim Blechmann 	if (runtime->sample_bits == 16)
68202bec490STim Blechmann 		/* 16 bit format */
68302bec490STim Blechmann 		chip->rmh.cmd[0] |= (STREAM_FMT_16b << STREAM_FMT_OFFSET);
68402bec490STim Blechmann 
68502bec490STim Blechmann 	if (snd_pcm_format_little_endian(runtime->format))
68602bec490STim Blechmann 		/* little endian/intel format */
68702bec490STim Blechmann 		chip->rmh.cmd[0] |= (STREAM_FMT_intel << STREAM_FMT_OFFSET);
68802bec490STim Blechmann 
68902bec490STim Blechmann 	chip->rmh.cmd[0] |= channels-1;
69002bec490STim Blechmann 
69102bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
6926336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
69302bec490STim Blechmann 
69402bec490STim Blechmann 	return err;
69502bec490STim Blechmann }
69602bec490STim Blechmann 
69702bec490STim Blechmann int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
69802bec490STim Blechmann 		    int *rstate)
69902bec490STim Blechmann {
70002bec490STim Blechmann 	int err;
70102bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
70202bec490STim Blechmann 
7036336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
70402bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
70502bec490STim Blechmann 
70602bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
70702bec490STim Blechmann 
70802bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
70902bec490STim Blechmann 
71002bec490STim Blechmann 	*rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE;
71102bec490STim Blechmann 
7126336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
71302bec490STim Blechmann 	return err;
71402bec490STim Blechmann }
71502bec490STim Blechmann 
71602bec490STim Blechmann int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
71702bec490STim Blechmann 			      u64 *r_bytepos)
71802bec490STim Blechmann {
71902bec490STim Blechmann 	int err;
72002bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
72102bec490STim Blechmann 
7226336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
72302bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
72402bec490STim Blechmann 
72502bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
72602bec490STim Blechmann 
72702bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
72802bec490STim Blechmann 
72902bec490STim Blechmann 	*r_bytepos = ((u64) (chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
73002bec490STim Blechmann 		      << 32)	     /* hi part */
73102bec490STim Blechmann 		+ chip->rmh.stat[1]; /* lo part */
73202bec490STim Blechmann 
7336336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
73402bec490STim Blechmann 	return err;
73502bec490STim Blechmann }
73602bec490STim Blechmann 
73702bec490STim Blechmann /* low-level buffer handling */
73802bec490STim Blechmann int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
73902bec490STim Blechmann 		   u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi,
74002bec490STim Blechmann 		   u32 *r_buffer_index)
74102bec490STim Blechmann {
74202bec490STim Blechmann 	int err;
74302bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
74402bec490STim Blechmann 
7456336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
74602bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER);
74702bec490STim Blechmann 
74802bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
74902bec490STim Blechmann 	chip->rmh.cmd[0] |= BF_NOTIFY_EOB; /* request interrupt notification */
75002bec490STim Blechmann 
75102bec490STim Blechmann 	/* todo: pause request, circular buffer */
75202bec490STim Blechmann 
75302bec490STim Blechmann 	chip->rmh.cmd[1] = buffer_size & MASK_DATA_SIZE;
75402bec490STim Blechmann 	chip->rmh.cmd[2] = buf_address_lo;
75502bec490STim Blechmann 
75602bec490STim Blechmann 	if (buf_address_hi) {
75702bec490STim Blechmann 		chip->rmh.cmd_len = 4;
75802bec490STim Blechmann 		chip->rmh.cmd[3] = buf_address_hi;
75902bec490STim Blechmann 		chip->rmh.cmd[0] |= BF_64BITS_ADR;
76002bec490STim Blechmann 	}
76102bec490STim Blechmann 
76202bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
76302bec490STim Blechmann 
76402bec490STim Blechmann 	if (err == 0) {
76502bec490STim Blechmann 		*r_buffer_index = chip->rmh.stat[0];
76602bec490STim Blechmann 		goto done;
76702bec490STim Blechmann 	}
76802bec490STim Blechmann 
76902bec490STim Blechmann 	if (err == EB_RBUFFERS_TABLE_OVERFLOW)
770be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev,
771be4e6d3cSTakashi Iwai 			"lx_buffer_give EB_RBUFFERS_TABLE_OVERFLOW\n");
77202bec490STim Blechmann 
77302bec490STim Blechmann 	if (err == EB_INVALID_STREAM)
774be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev,
775be4e6d3cSTakashi Iwai 			"lx_buffer_give EB_INVALID_STREAM\n");
77602bec490STim Blechmann 
77702bec490STim Blechmann 	if (err == EB_CMD_REFUSED)
778be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev,
779be4e6d3cSTakashi Iwai 			"lx_buffer_give EB_CMD_REFUSED\n");
78002bec490STim Blechmann 
78102bec490STim Blechmann  done:
7826336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
78302bec490STim Blechmann 	return err;
78402bec490STim Blechmann }
78502bec490STim Blechmann 
78602bec490STim Blechmann int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
78702bec490STim Blechmann 		   u32 *r_buffer_size)
78802bec490STim Blechmann {
78902bec490STim Blechmann 	int err;
79002bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
79102bec490STim Blechmann 
7926336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
79302bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
79402bec490STim Blechmann 
79502bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
79602bec490STim Blechmann 	chip->rmh.cmd[0] |= MASK_BUFFER_ID; /* ask for the current buffer: the
79702bec490STim Blechmann 					     * microblaze will seek for it */
79802bec490STim Blechmann 
79902bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
80002bec490STim Blechmann 
80102bec490STim Blechmann 	if (err == 0)
80202bec490STim Blechmann 		*r_buffer_size = chip->rmh.stat[0]  & MASK_DATA_SIZE;
80302bec490STim Blechmann 
8046336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
80502bec490STim Blechmann 	return err;
80602bec490STim Blechmann }
80702bec490STim Blechmann 
80802bec490STim Blechmann int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
80902bec490STim Blechmann 		     u32 buffer_index)
81002bec490STim Blechmann {
81102bec490STim Blechmann 	int err;
81202bec490STim Blechmann 	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
81302bec490STim Blechmann 
8146336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
81502bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
81602bec490STim Blechmann 
81702bec490STim Blechmann 	chip->rmh.cmd[0] |= pipe_cmd;
81802bec490STim Blechmann 	chip->rmh.cmd[0] |= buffer_index;
81902bec490STim Blechmann 
82002bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
82102bec490STim Blechmann 
8226336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
82302bec490STim Blechmann 	return err;
82402bec490STim Blechmann }
82502bec490STim Blechmann 
82602bec490STim Blechmann 
82702bec490STim Blechmann /* low-level gain/peak handling
82802bec490STim Blechmann  *
82902bec490STim Blechmann  * \todo: can we unmute capture/playback channels independently?
83002bec490STim Blechmann  *
83102bec490STim Blechmann  * */
83202bec490STim Blechmann int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
83302bec490STim Blechmann {
83402bec490STim Blechmann 	int err;
83502bec490STim Blechmann 	/* bit set to 1: channel muted */
83602bec490STim Blechmann 	u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU;
83702bec490STim Blechmann 
8386336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
83902bec490STim Blechmann 	lx_message_init(&chip->rmh, CMD_0D_SET_MUTE);
84002bec490STim Blechmann 
84102bec490STim Blechmann 	chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0);
84202bec490STim Blechmann 
84302bec490STim Blechmann 	chip->rmh.cmd[1] = (u32)(mute_mask >> (u64)32);	       /* hi part */
84402bec490STim Blechmann 	chip->rmh.cmd[2] = (u32)(mute_mask & (u64)0xFFFFFFFF); /* lo part */
84502bec490STim Blechmann 
846be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev,
847be4e6d3cSTakashi Iwai 		"mute %x %x %x\n", chip->rmh.cmd[0], chip->rmh.cmd[1],
84802bec490STim Blechmann 		   chip->rmh.cmd[2]);
84902bec490STim Blechmann 
85002bec490STim Blechmann 	err = lx_message_send_atomic(chip, &chip->rmh);
85102bec490STim Blechmann 
8526336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
85302bec490STim Blechmann 	return err;
85402bec490STim Blechmann }
85502bec490STim Blechmann 
8562f200ce2STakashi Iwai static const u32 peak_map[] = {
85702bec490STim Blechmann 	0x00000109, /* -90.308dB */
85802bec490STim Blechmann 	0x0000083B, /* -72.247dB */
85902bec490STim Blechmann 	0x000020C4, /* -60.205dB */
86002bec490STim Blechmann 	0x00008273, /* -48.030dB */
86102bec490STim Blechmann 	0x00020756, /* -36.005dB */
86202bec490STim Blechmann 	0x00040C37, /* -30.001dB */
86302bec490STim Blechmann 	0x00081385, /* -24.002dB */
86402bec490STim Blechmann 	0x00101D3F, /* -18.000dB */
86502bec490STim Blechmann 	0x0016C310, /* -15.000dB */
86602bec490STim Blechmann 	0x002026F2, /* -12.001dB */
86702bec490STim Blechmann 	0x002D6A86, /* -9.000dB */
86802bec490STim Blechmann 	0x004026E6, /* -6.004dB */
86902bec490STim Blechmann 	0x005A9DF6, /* -3.000dB */
87002bec490STim Blechmann 	0x0065AC8B, /* -2.000dB */
87102bec490STim Blechmann 	0x00721481, /* -1.000dB */
87202bec490STim Blechmann 	0x007FFFFF, /* FS */
87302bec490STim Blechmann };
87402bec490STim Blechmann 
87502bec490STim Blechmann int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
87602bec490STim Blechmann 		   u32 *r_levels)
87702bec490STim Blechmann {
87802bec490STim Blechmann 	int err = 0;
87902bec490STim Blechmann 	int i;
88002bec490STim Blechmann 
8816336c20cSTakashi Iwai 	mutex_lock(&chip->msg_lock);
88202bec490STim Blechmann 	for (i = 0; i < channels; i += 4) {
88302bec490STim Blechmann 		u32 s0, s1, s2, s3;
88402bec490STim Blechmann 
88502bec490STim Blechmann 		lx_message_init(&chip->rmh, CMD_12_GET_PEAK);
88602bec490STim Blechmann 		chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, i);
88702bec490STim Blechmann 
88802bec490STim Blechmann 		err = lx_message_send_atomic(chip, &chip->rmh);
88902bec490STim Blechmann 
89002bec490STim Blechmann 		if (err == 0) {
89102bec490STim Blechmann 			s0 = peak_map[chip->rmh.stat[0] & 0x0F];
89202bec490STim Blechmann 			s1 = peak_map[(chip->rmh.stat[0] >>  4) & 0xf];
89302bec490STim Blechmann 			s2 = peak_map[(chip->rmh.stat[0] >>  8) & 0xf];
89402bec490STim Blechmann 			s3 = peak_map[(chip->rmh.stat[0] >>  12) & 0xf];
89502bec490STim Blechmann 		} else
89602bec490STim Blechmann 			s0 = s1 = s2 = s3 = 0;
89702bec490STim Blechmann 
89802bec490STim Blechmann 		r_levels[0] = s0;
89902bec490STim Blechmann 		r_levels[1] = s1;
90002bec490STim Blechmann 		r_levels[2] = s2;
90102bec490STim Blechmann 		r_levels[3] = s3;
90202bec490STim Blechmann 
90302bec490STim Blechmann 		r_levels += 4;
90402bec490STim Blechmann 	}
90502bec490STim Blechmann 
9066336c20cSTakashi Iwai 	mutex_unlock(&chip->msg_lock);
90702bec490STim Blechmann 	return err;
90802bec490STim Blechmann }
90902bec490STim Blechmann 
91002bec490STim Blechmann /* interrupt handling */
91102bec490STim Blechmann #define PCX_IRQ_NONE 0
912c546ca95SMaxime Ripard #define IRQCS_ACTIVE_PCIDB	BIT(13)
913c546ca95SMaxime Ripard #define IRQCS_ENABLE_PCIIRQ	BIT(8)
914c546ca95SMaxime Ripard #define IRQCS_ENABLE_PCIDB	BIT(9)
91502bec490STim Blechmann 
91602bec490STim Blechmann static u32 lx_interrupt_test_ack(struct lx6464es *chip)
91702bec490STim Blechmann {
91802bec490STim Blechmann 	u32 irqcs = lx_plx_reg_read(chip, ePLX_IRQCS);
91902bec490STim Blechmann 
92002bec490STim Blechmann 	/* Test if PCI Doorbell interrupt is active */
92102bec490STim Blechmann 	if (irqcs & IRQCS_ACTIVE_PCIDB)	{
92202bec490STim Blechmann 		u32 temp;
92302bec490STim Blechmann 		irqcs = PCX_IRQ_NONE;
92402bec490STim Blechmann 
92502bec490STim Blechmann 		while ((temp = lx_plx_reg_read(chip, ePLX_L2PCIDB))) {
92602bec490STim Blechmann 			/* RAZ interrupt */
92702bec490STim Blechmann 			irqcs |= temp;
92802bec490STim Blechmann 			lx_plx_reg_write(chip, ePLX_L2PCIDB, temp);
92902bec490STim Blechmann 		}
93002bec490STim Blechmann 
93102bec490STim Blechmann 		return irqcs;
93202bec490STim Blechmann 	}
93302bec490STim Blechmann 	return PCX_IRQ_NONE;
93402bec490STim Blechmann }
93502bec490STim Blechmann 
93602bec490STim Blechmann static int lx_interrupt_ack(struct lx6464es *chip, u32 *r_irqsrc,
93702bec490STim Blechmann 			    int *r_async_pending, int *r_async_escmd)
93802bec490STim Blechmann {
93902bec490STim Blechmann 	u32 irq_async;
94002bec490STim Blechmann 	u32 irqsrc = lx_interrupt_test_ack(chip);
94102bec490STim Blechmann 
94202bec490STim Blechmann 	if (irqsrc == PCX_IRQ_NONE)
94302bec490STim Blechmann 		return 0;
94402bec490STim Blechmann 
94502bec490STim Blechmann 	*r_irqsrc = irqsrc;
94602bec490STim Blechmann 
94702bec490STim Blechmann 	irq_async = irqsrc & MASK_SYS_ASYNC_EVENTS; /* + EtherSound response
94802bec490STim Blechmann 						     * (set by xilinx) + EOB */
94902bec490STim Blechmann 
95002bec490STim Blechmann 	if (irq_async & MASK_SYS_STATUS_ESA) {
95102bec490STim Blechmann 		irq_async &= ~MASK_SYS_STATUS_ESA;
95202bec490STim Blechmann 		*r_async_escmd = 1;
95302bec490STim Blechmann 	}
95402bec490STim Blechmann 
95502bec490STim Blechmann 	if (irq_async) {
956be4e6d3cSTakashi Iwai 		/* dev_dbg(chip->card->dev, "interrupt: async event pending\n"); */
95702bec490STim Blechmann 		*r_async_pending = 1;
95802bec490STim Blechmann 	}
95902bec490STim Blechmann 
96002bec490STim Blechmann 	return 1;
96102bec490STim Blechmann }
96202bec490STim Blechmann 
96302bec490STim Blechmann static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
96402bec490STim Blechmann 					    int *r_freq_changed,
96502bec490STim Blechmann 					    u64 *r_notified_in_pipe_mask,
96602bec490STim Blechmann 					    u64 *r_notified_out_pipe_mask)
96702bec490STim Blechmann {
96802bec490STim Blechmann 	int err;
96902bec490STim Blechmann 	u32 stat[9];		/* answer from CMD_04_GET_EVENT */
97002bec490STim Blechmann 
97138137a06SMaxime Ripard 	/* We can optimize this to not read dumb events.
97238137a06SMaxime Ripard 	 * Answer words are in the following order:
97338137a06SMaxime Ripard 	 * Stat[0]	general status
97438137a06SMaxime Ripard 	 * Stat[1]	end of buffer OUT pF
97538137a06SMaxime Ripard 	 * Stat[2]	end of buffer OUT pf
97638137a06SMaxime Ripard 	 * Stat[3]	end of buffer IN pF
97738137a06SMaxime Ripard 	 * Stat[4]	end of buffer IN pf
97838137a06SMaxime Ripard 	 * Stat[5]	MSB underrun
97938137a06SMaxime Ripard 	 * Stat[6]	LSB underrun
98038137a06SMaxime Ripard 	 * Stat[7]	MSB overrun
98138137a06SMaxime Ripard 	 * Stat[8]	LSB overrun
98202bec490STim Blechmann 	 * */
98302bec490STim Blechmann 
98402bec490STim Blechmann 	int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
98502bec490STim Blechmann 	int eb_pending_in  = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
98602bec490STim Blechmann 
98702bec490STim Blechmann 	*r_freq_changed = (irqsrc & MASK_SYS_STATUS_FREQ) ? 1 : 0;
98802bec490STim Blechmann 
98902bec490STim Blechmann 	err = lx_dsp_read_async_events(chip, stat);
99002bec490STim Blechmann 	if (err < 0)
99102bec490STim Blechmann 		return err;
99202bec490STim Blechmann 
99302bec490STim Blechmann 	if (eb_pending_in) {
99402bec490STim Blechmann 		*r_notified_in_pipe_mask = ((u64)stat[3] << 32)
99502bec490STim Blechmann 			+ stat[4];
996be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "interrupt: EOBI pending %llx\n",
99702bec490STim Blechmann 			    *r_notified_in_pipe_mask);
99802bec490STim Blechmann 	}
99902bec490STim Blechmann 	if (eb_pending_out) {
100002bec490STim Blechmann 		*r_notified_out_pipe_mask = ((u64)stat[1] << 32)
100102bec490STim Blechmann 			+ stat[2];
1002be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "interrupt: EOBO pending %llx\n",
100302bec490STim Blechmann 			    *r_notified_out_pipe_mask);
100402bec490STim Blechmann 	}
100502bec490STim Blechmann 
100602bec490STim Blechmann 	/* todo: handle xrun notification */
100702bec490STim Blechmann 
100802bec490STim Blechmann 	return err;
100902bec490STim Blechmann }
101002bec490STim Blechmann 
101102bec490STim Blechmann static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
101202bec490STim Blechmann 					   struct lx_stream *lx_stream)
101302bec490STim Blechmann {
101402bec490STim Blechmann 	struct snd_pcm_substream *substream = lx_stream->stream;
1015f7467452STim Blechmann 	const unsigned int is_capture = lx_stream->is_capture;
101602bec490STim Blechmann 	int err;
101702bec490STim Blechmann 
101802bec490STim Blechmann 	const u32 channels = substream->runtime->channels;
101902bec490STim Blechmann 	const u32 bytes_per_frame = channels * 3;
102002bec490STim Blechmann 	const u32 period_size = substream->runtime->period_size;
102102bec490STim Blechmann 	const u32 period_bytes = period_size * bytes_per_frame;
102202bec490STim Blechmann 	const u32 pos = lx_stream->frame_pos;
102302bec490STim Blechmann 	const u32 next_pos = ((pos+1) == substream->runtime->periods) ?
102402bec490STim Blechmann 		0 : pos + 1;
102502bec490STim Blechmann 
102602bec490STim Blechmann 	dma_addr_t buf = substream->dma_buffer.addr + pos * period_bytes;
102702bec490STim Blechmann 	u32 buf_hi = 0;
102802bec490STim Blechmann 	u32 buf_lo = 0;
102902bec490STim Blechmann 	u32 buffer_index = 0;
103002bec490STim Blechmann 
103102bec490STim Blechmann 	u32 needed, freed;
103202bec490STim Blechmann 	u32 size_array[MAX_STREAM_BUFFER];
103302bec490STim Blechmann 
1034be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_interrupt_request_new_buffer\n");
103502bec490STim Blechmann 
10366336c20cSTakashi Iwai 	mutex_lock(&chip->lock);
103702bec490STim Blechmann 
103802bec490STim Blechmann 	err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
1039be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev,
1040be4e6d3cSTakashi Iwai 		"interrupt: needed %d, freed %d\n", needed, freed);
104102bec490STim Blechmann 
104202bec490STim Blechmann 	unpack_pointer(buf, &buf_lo, &buf_hi);
104302bec490STim Blechmann 	err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi,
104402bec490STim Blechmann 			     &buffer_index);
1045be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev,
1046be4e6d3cSTakashi Iwai 		"interrupt: gave buffer index %x on 0x%lx (%d bytes)\n",
1047293db842STakashi Iwai 		    buffer_index, (unsigned long)buf, period_bytes);
104802bec490STim Blechmann 
104902bec490STim Blechmann 	lx_stream->frame_pos = next_pos;
10506336c20cSTakashi Iwai 	mutex_unlock(&chip->lock);
105102bec490STim Blechmann 
105202bec490STim Blechmann 	return err;
105302bec490STim Blechmann }
105402bec490STim Blechmann 
105502bec490STim Blechmann irqreturn_t lx_interrupt(int irq, void *dev_id)
105602bec490STim Blechmann {
105702bec490STim Blechmann 	struct lx6464es *chip = dev_id;
105802bec490STim Blechmann 	int async_pending, async_escmd;
105902bec490STim Blechmann 	u32 irqsrc;
10606336c20cSTakashi Iwai 	bool wake_thread = false;
106102bec490STim Blechmann 
1062be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev,
1063be4e6d3cSTakashi Iwai 		"**************************************************\n");
106402bec490STim Blechmann 
106502bec490STim Blechmann 	if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) {
1066be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "IRQ_NONE\n");
106702bec490STim Blechmann 		return IRQ_NONE; /* this device did not cause the interrupt */
106802bec490STim Blechmann 	}
106902bec490STim Blechmann 
107002bec490STim Blechmann 	if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
10716336c20cSTakashi Iwai 		return IRQ_HANDLED;
107202bec490STim Blechmann 
107302bec490STim Blechmann 	if (irqsrc & MASK_SYS_STATUS_EOBI)
107468e440bbSMaxime Ripard 		dev_dbg(chip->card->dev, "interrupt: EOBI\n");
107502bec490STim Blechmann 
107602bec490STim Blechmann 	if (irqsrc & MASK_SYS_STATUS_EOBO)
1077be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "interrupt: EOBO\n");
107802bec490STim Blechmann 
107902bec490STim Blechmann 	if (irqsrc & MASK_SYS_STATUS_URUN)
1080be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "interrupt: URUN\n");
108102bec490STim Blechmann 
108202bec490STim Blechmann 	if (irqsrc & MASK_SYS_STATUS_ORUN)
1083be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "interrupt: ORUN\n");
108402bec490STim Blechmann 
108502bec490STim Blechmann 	if (async_pending) {
10866336c20cSTakashi Iwai 		wake_thread = true;
10876336c20cSTakashi Iwai 		chip->irqsrc = irqsrc;
108802bec490STim Blechmann 	}
108902bec490STim Blechmann 
109002bec490STim Blechmann 	if (async_escmd) {
109102bec490STim Blechmann 		/* backdoor for ethersound commands
109202bec490STim Blechmann 		 *
109302bec490STim Blechmann 		 * for now, we do not need this
109402bec490STim Blechmann 		 *
109502bec490STim Blechmann 		 * */
109602bec490STim Blechmann 
1097be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "interrupt requests escmd handling\n");
109802bec490STim Blechmann 	}
109902bec490STim Blechmann 
11006336c20cSTakashi Iwai 	return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
11016336c20cSTakashi Iwai }
11026336c20cSTakashi Iwai 
11036336c20cSTakashi Iwai irqreturn_t lx_threaded_irq(int irq, void *dev_id)
11046336c20cSTakashi Iwai {
11056336c20cSTakashi Iwai 	struct lx6464es *chip = dev_id;
11066336c20cSTakashi Iwai 	u64 notified_in_pipe_mask = 0;
11076336c20cSTakashi Iwai 	u64 notified_out_pipe_mask = 0;
11086336c20cSTakashi Iwai 	int freq_changed;
11096336c20cSTakashi Iwai 	int err;
11106336c20cSTakashi Iwai 
11116336c20cSTakashi Iwai 	/* handle async events */
11126336c20cSTakashi Iwai 	err = lx_interrupt_handle_async_events(chip, chip->irqsrc,
11136336c20cSTakashi Iwai 					       &freq_changed,
11146336c20cSTakashi Iwai 					       &notified_in_pipe_mask,
11156336c20cSTakashi Iwai 					       &notified_out_pipe_mask);
11166336c20cSTakashi Iwai 	if (err)
11176336c20cSTakashi Iwai 		dev_err(chip->card->dev, "error handling async events\n");
11186336c20cSTakashi Iwai 
11196336c20cSTakashi Iwai 	if (notified_in_pipe_mask) {
11206336c20cSTakashi Iwai 		struct lx_stream *lx_stream = &chip->capture_stream;
11216336c20cSTakashi Iwai 
11226336c20cSTakashi Iwai 		dev_dbg(chip->card->dev,
11236336c20cSTakashi Iwai 			"requesting audio transfer for capture\n");
11246336c20cSTakashi Iwai 		err = lx_interrupt_request_new_buffer(chip, lx_stream);
11256336c20cSTakashi Iwai 		if (err < 0)
11266336c20cSTakashi Iwai 			dev_err(chip->card->dev,
11276336c20cSTakashi Iwai 				"cannot request new buffer for capture\n");
11286336c20cSTakashi Iwai 		snd_pcm_period_elapsed(lx_stream->stream);
11296336c20cSTakashi Iwai 	}
11306336c20cSTakashi Iwai 
11316336c20cSTakashi Iwai 	if (notified_out_pipe_mask) {
11326336c20cSTakashi Iwai 		struct lx_stream *lx_stream = &chip->playback_stream;
11336336c20cSTakashi Iwai 
11346336c20cSTakashi Iwai 		dev_dbg(chip->card->dev,
11356336c20cSTakashi Iwai 			"requesting audio transfer for playback\n");
11366336c20cSTakashi Iwai 		err = lx_interrupt_request_new_buffer(chip, lx_stream);
11376336c20cSTakashi Iwai 		if (err < 0)
11386336c20cSTakashi Iwai 			dev_err(chip->card->dev,
11396336c20cSTakashi Iwai 				"cannot request new buffer for playback\n");
11406336c20cSTakashi Iwai 		snd_pcm_period_elapsed(lx_stream->stream);
11416336c20cSTakashi Iwai 	}
11426336c20cSTakashi Iwai 
11436336c20cSTakashi Iwai 	return IRQ_HANDLED;
114402bec490STim Blechmann }
114502bec490STim Blechmann 
114602bec490STim Blechmann 
114702bec490STim Blechmann static void lx_irq_set(struct lx6464es *chip, int enable)
114802bec490STim Blechmann {
114902bec490STim Blechmann 	u32 reg = lx_plx_reg_read(chip, ePLX_IRQCS);
115002bec490STim Blechmann 
115102bec490STim Blechmann 	/* enable/disable interrupts
115202bec490STim Blechmann 	 *
115302bec490STim Blechmann 	 * Set the Doorbell and PCI interrupt enable bits
115402bec490STim Blechmann 	 *
115502bec490STim Blechmann 	 * */
115602bec490STim Blechmann 	if (enable)
115702bec490STim Blechmann 		reg |=  (IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB);
115802bec490STim Blechmann 	else
115902bec490STim Blechmann 		reg &= ~(IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB);
116002bec490STim Blechmann 	lx_plx_reg_write(chip, ePLX_IRQCS, reg);
116102bec490STim Blechmann }
116202bec490STim Blechmann 
116302bec490STim Blechmann void lx_irq_enable(struct lx6464es *chip)
116402bec490STim Blechmann {
1165be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_irq_enable\n");
116602bec490STim Blechmann 	lx_irq_set(chip, 1);
116702bec490STim Blechmann }
116802bec490STim Blechmann 
116902bec490STim Blechmann void lx_irq_disable(struct lx6464es *chip)
117002bec490STim Blechmann {
1171be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_irq_disable\n");
117202bec490STim Blechmann 	lx_irq_set(chip, 0);
117302bec490STim Blechmann }
1174