162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * cmt_speech.c - HSI CMT speech driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2008,2009,2010 Nokia Corporation. All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Contact: Kai Vehmanen <kai.vehmanen@nokia.com>
862306a36Sopenharmony_ci * Original author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/device.h>
1662306a36Sopenharmony_ci#include <linux/miscdevice.h>
1762306a36Sopenharmony_ci#include <linux/mm.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/fs.h>
2062306a36Sopenharmony_ci#include <linux/poll.h>
2162306a36Sopenharmony_ci#include <linux/sched/signal.h>
2262306a36Sopenharmony_ci#include <linux/ioctl.h>
2362306a36Sopenharmony_ci#include <linux/uaccess.h>
2462306a36Sopenharmony_ci#include <linux/pm_qos.h>
2562306a36Sopenharmony_ci#include <linux/hsi/hsi.h>
2662306a36Sopenharmony_ci#include <linux/hsi/ssi_protocol.h>
2762306a36Sopenharmony_ci#include <linux/hsi/cs-protocol.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define CS_MMAP_SIZE	PAGE_SIZE
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct char_queue {
3262306a36Sopenharmony_ci	struct list_head	list;
3362306a36Sopenharmony_ci	u32			msg;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct cs_char {
3762306a36Sopenharmony_ci	unsigned int		opened;
3862306a36Sopenharmony_ci	struct hsi_client	*cl;
3962306a36Sopenharmony_ci	struct cs_hsi_iface	*hi;
4062306a36Sopenharmony_ci	struct list_head	chardev_queue;
4162306a36Sopenharmony_ci	struct list_head	dataind_queue;
4262306a36Sopenharmony_ci	int			dataind_pending;
4362306a36Sopenharmony_ci	/* mmap things */
4462306a36Sopenharmony_ci	unsigned long		mmap_base;
4562306a36Sopenharmony_ci	unsigned long		mmap_size;
4662306a36Sopenharmony_ci	spinlock_t		lock;
4762306a36Sopenharmony_ci	struct fasync_struct	*async_queue;
4862306a36Sopenharmony_ci	wait_queue_head_t	wait;
4962306a36Sopenharmony_ci	/* hsi channel ids */
5062306a36Sopenharmony_ci	int                     channel_id_cmd;
5162306a36Sopenharmony_ci	int                     channel_id_data;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_READING	1
5562306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_WRITING	(1 << 1)
5662306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_POLL		(1 << 2)
5762306a36Sopenharmony_ci#define SSI_CHANNEL_STATE_ERROR		(1 << 3)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define TARGET_MASK			0xf000000
6062306a36Sopenharmony_ci#define TARGET_REMOTE			(1 << CS_DOMAIN_SHIFT)
6162306a36Sopenharmony_ci#define TARGET_LOCAL			0
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Number of pre-allocated commands buffers */
6462306a36Sopenharmony_ci#define CS_MAX_CMDS		        4
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * During data transfers, transactions must be handled
6862306a36Sopenharmony_ci * within 20ms (fixed value in cmtspeech HSI protocol)
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci#define CS_QOS_LATENCY_FOR_DATA_USEC	20000
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Timeout to wait for pending HSI transfers to complete */
7362306a36Sopenharmony_ci#define CS_HSI_TRANSFER_TIMEOUT_MS      500
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define RX_PTR_BOUNDARY_SHIFT		8
7762306a36Sopenharmony_ci#define RX_PTR_MAX_SHIFT		(RX_PTR_BOUNDARY_SHIFT + \
7862306a36Sopenharmony_ci						CS_MAX_BUFFERS_SHIFT)
7962306a36Sopenharmony_cistruct cs_hsi_iface {
8062306a36Sopenharmony_ci	struct hsi_client		*cl;
8162306a36Sopenharmony_ci	struct hsi_client		*master;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	unsigned int			iface_state;
8462306a36Sopenharmony_ci	unsigned int			wakeline_state;
8562306a36Sopenharmony_ci	unsigned int			control_state;
8662306a36Sopenharmony_ci	unsigned int			data_state;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* state exposed to application */
8962306a36Sopenharmony_ci	struct cs_mmap_config_block	*mmap_cfg;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	unsigned long			mmap_base;
9262306a36Sopenharmony_ci	unsigned long			mmap_size;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	unsigned int			rx_slot;
9562306a36Sopenharmony_ci	unsigned int			tx_slot;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* note: for security reasons, we do not trust the contents of
9862306a36Sopenharmony_ci	 * mmap_cfg, but instead duplicate the variables here */
9962306a36Sopenharmony_ci	unsigned int			buf_size;
10062306a36Sopenharmony_ci	unsigned int			rx_bufs;
10162306a36Sopenharmony_ci	unsigned int			tx_bufs;
10262306a36Sopenharmony_ci	unsigned int			rx_ptr_boundary;
10362306a36Sopenharmony_ci	unsigned int			rx_offsets[CS_MAX_BUFFERS];
10462306a36Sopenharmony_ci	unsigned int			tx_offsets[CS_MAX_BUFFERS];
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* size of aligned memory blocks */
10762306a36Sopenharmony_ci	unsigned int			slot_size;
10862306a36Sopenharmony_ci	unsigned int			flags;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	struct list_head		cmdqueue;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	struct hsi_msg			*data_rx_msg;
11362306a36Sopenharmony_ci	struct hsi_msg			*data_tx_msg;
11462306a36Sopenharmony_ci	wait_queue_head_t		datawait;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	struct pm_qos_request           pm_qos_req;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	spinlock_t			lock;
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic struct cs_char cs_char_data;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void cs_hsi_read_on_control(struct cs_hsi_iface *hi);
12462306a36Sopenharmony_cistatic void cs_hsi_read_on_data(struct cs_hsi_iface *hi);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic inline void rx_ptr_shift_too_big(void)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	BUILD_BUG_ON((1LLU << RX_PTR_MAX_SHIFT) > UINT_MAX);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void cs_notify(u32 message, struct list_head *head)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct char_queue *entry;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	spin_lock(&cs_char_data.lock);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!cs_char_data.opened) {
13862306a36Sopenharmony_ci		spin_unlock(&cs_char_data.lock);
13962306a36Sopenharmony_ci		goto out;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
14362306a36Sopenharmony_ci	if (!entry) {
14462306a36Sopenharmony_ci		dev_err(&cs_char_data.cl->device,
14562306a36Sopenharmony_ci			"Can't allocate new entry for the queue.\n");
14662306a36Sopenharmony_ci		spin_unlock(&cs_char_data.lock);
14762306a36Sopenharmony_ci		goto out;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	entry->msg = message;
15162306a36Sopenharmony_ci	list_add_tail(&entry->list, head);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	spin_unlock(&cs_char_data.lock);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	wake_up_interruptible(&cs_char_data.wait);
15662306a36Sopenharmony_ci	kill_fasync(&cs_char_data.async_queue, SIGIO, POLL_IN);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciout:
15962306a36Sopenharmony_ci	return;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic u32 cs_pop_entry(struct list_head *head)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct char_queue *entry;
16562306a36Sopenharmony_ci	u32 data;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	entry = list_entry(head->next, struct char_queue, list);
16862306a36Sopenharmony_ci	data = entry->msg;
16962306a36Sopenharmony_ci	list_del(&entry->list);
17062306a36Sopenharmony_ci	kfree(entry);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return data;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void cs_notify_control(u32 message)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	cs_notify(message, &cs_char_data.chardev_queue);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void cs_notify_data(u32 message, int maxlength)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	cs_notify(message, &cs_char_data.dataind_queue);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_lock(&cs_char_data.lock);
18562306a36Sopenharmony_ci	cs_char_data.dataind_pending++;
18662306a36Sopenharmony_ci	while (cs_char_data.dataind_pending > maxlength &&
18762306a36Sopenharmony_ci				!list_empty(&cs_char_data.dataind_queue)) {
18862306a36Sopenharmony_ci		dev_dbg(&cs_char_data.cl->device, "data notification "
18962306a36Sopenharmony_ci		"queue overrun (%u entries)\n", cs_char_data.dataind_pending);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		cs_pop_entry(&cs_char_data.dataind_queue);
19262306a36Sopenharmony_ci		cs_char_data.dataind_pending--;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	spin_unlock(&cs_char_data.lock);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic inline void cs_set_cmd(struct hsi_msg *msg, u32 cmd)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	u32 *data = sg_virt(msg->sgt.sgl);
20062306a36Sopenharmony_ci	*data = cmd;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic inline u32 cs_get_cmd(struct hsi_msg *msg)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u32 *data = sg_virt(msg->sgt.sgl);
20662306a36Sopenharmony_ci	return *data;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void cs_release_cmd(struct hsi_msg *msg)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	list_add_tail(&msg->link, &hi->cmdqueue);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void cs_cmd_destructor(struct hsi_msg *msg)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	spin_lock(&hi->lock);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	dev_dbg(&cs_char_data.cl->device, "control cmd destructor\n");
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (hi->iface_state != CS_STATE_CLOSED)
22562306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Cmd flushed while driver active\n");
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (msg->ttype == HSI_MSG_READ)
22862306a36Sopenharmony_ci		hi->control_state &=
22962306a36Sopenharmony_ci			~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING);
23062306a36Sopenharmony_ci	else if (msg->ttype == HSI_MSG_WRITE &&
23162306a36Sopenharmony_ci			hi->control_state & SSI_CHANNEL_STATE_WRITING)
23262306a36Sopenharmony_ci		hi->control_state &= ~SSI_CHANNEL_STATE_WRITING;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	cs_release_cmd(msg);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	spin_unlock(&hi->lock);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic struct hsi_msg *cs_claim_cmd(struct cs_hsi_iface* ssi)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct hsi_msg *msg;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	BUG_ON(list_empty(&ssi->cmdqueue));
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link);
24662306a36Sopenharmony_ci	list_del(&msg->link);
24762306a36Sopenharmony_ci	msg->destructor = cs_cmd_destructor;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return msg;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void cs_free_cmds(struct cs_hsi_iface *ssi)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct hsi_msg *msg, *tmp;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) {
25762306a36Sopenharmony_ci		list_del(&msg->link);
25862306a36Sopenharmony_ci		msg->destructor = NULL;
25962306a36Sopenharmony_ci		kfree(sg_virt(msg->sgt.sgl));
26062306a36Sopenharmony_ci		hsi_free_msg(msg);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int cs_alloc_cmds(struct cs_hsi_iface *hi)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct hsi_msg *msg;
26762306a36Sopenharmony_ci	u32 *buf;
26862306a36Sopenharmony_ci	unsigned int i;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	INIT_LIST_HEAD(&hi->cmdqueue);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	for (i = 0; i < CS_MAX_CMDS; i++) {
27362306a36Sopenharmony_ci		msg = hsi_alloc_msg(1, GFP_KERNEL);
27462306a36Sopenharmony_ci		if (!msg)
27562306a36Sopenharmony_ci			goto out;
27662306a36Sopenharmony_ci		buf = kmalloc(sizeof(*buf), GFP_KERNEL);
27762306a36Sopenharmony_ci		if (!buf) {
27862306a36Sopenharmony_ci			hsi_free_msg(msg);
27962306a36Sopenharmony_ci			goto out;
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci		sg_init_one(msg->sgt.sgl, buf, sizeof(*buf));
28262306a36Sopenharmony_ci		msg->channel = cs_char_data.channel_id_cmd;
28362306a36Sopenharmony_ci		msg->context = hi;
28462306a36Sopenharmony_ci		list_add_tail(&msg->link, &hi->cmdqueue);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ciout:
29062306a36Sopenharmony_ci	cs_free_cmds(hi);
29162306a36Sopenharmony_ci	return -ENOMEM;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void cs_hsi_data_destructor(struct hsi_msg *msg)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
29762306a36Sopenharmony_ci	const char *dir = (msg->ttype == HSI_MSG_READ) ? "TX" : "RX";
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	dev_dbg(&cs_char_data.cl->device, "Freeing data %s message\n", dir);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	spin_lock(&hi->lock);
30262306a36Sopenharmony_ci	if (hi->iface_state != CS_STATE_CLOSED)
30362306a36Sopenharmony_ci		dev_err(&cs_char_data.cl->device,
30462306a36Sopenharmony_ci				"Data %s flush while device active\n", dir);
30562306a36Sopenharmony_ci	if (msg->ttype == HSI_MSG_READ)
30662306a36Sopenharmony_ci		hi->data_state &=
30762306a36Sopenharmony_ci			~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING);
30862306a36Sopenharmony_ci	else
30962306a36Sopenharmony_ci		hi->data_state &= ~SSI_CHANNEL_STATE_WRITING;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	msg->status = HSI_STATUS_COMPLETED;
31262306a36Sopenharmony_ci	if (unlikely(waitqueue_active(&hi->datawait)))
31362306a36Sopenharmony_ci		wake_up_interruptible(&hi->datawait);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	spin_unlock(&hi->lock);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int cs_hsi_alloc_data(struct cs_hsi_iface *hi)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct hsi_msg *txmsg, *rxmsg;
32162306a36Sopenharmony_ci	int res = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	rxmsg = hsi_alloc_msg(1, GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!rxmsg) {
32562306a36Sopenharmony_ci		res = -ENOMEM;
32662306a36Sopenharmony_ci		goto out1;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	rxmsg->channel = cs_char_data.channel_id_data;
32962306a36Sopenharmony_ci	rxmsg->destructor = cs_hsi_data_destructor;
33062306a36Sopenharmony_ci	rxmsg->context = hi;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	txmsg = hsi_alloc_msg(1, GFP_KERNEL);
33362306a36Sopenharmony_ci	if (!txmsg) {
33462306a36Sopenharmony_ci		res = -ENOMEM;
33562306a36Sopenharmony_ci		goto out2;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	txmsg->channel = cs_char_data.channel_id_data;
33862306a36Sopenharmony_ci	txmsg->destructor = cs_hsi_data_destructor;
33962306a36Sopenharmony_ci	txmsg->context = hi;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	hi->data_rx_msg = rxmsg;
34262306a36Sopenharmony_ci	hi->data_tx_msg = txmsg;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ciout2:
34762306a36Sopenharmony_ci	hsi_free_msg(rxmsg);
34862306a36Sopenharmony_ciout1:
34962306a36Sopenharmony_ci	return res;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void cs_hsi_free_data_msg(struct hsi_msg *msg)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	WARN_ON(msg->status != HSI_STATUS_COMPLETED &&
35562306a36Sopenharmony_ci					msg->status != HSI_STATUS_ERROR);
35662306a36Sopenharmony_ci	hsi_free_msg(msg);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void cs_hsi_free_data(struct cs_hsi_iface *hi)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	cs_hsi_free_data_msg(hi->data_rx_msg);
36262306a36Sopenharmony_ci	cs_hsi_free_data_msg(hi->data_tx_msg);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic inline void __cs_hsi_error_pre(struct cs_hsi_iface *hi,
36662306a36Sopenharmony_ci					struct hsi_msg *msg, const char *info,
36762306a36Sopenharmony_ci					unsigned int *state)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	spin_lock(&hi->lock);
37062306a36Sopenharmony_ci	dev_err(&hi->cl->device, "HSI %s error, msg %d, state %u\n",
37162306a36Sopenharmony_ci		info, msg->status, *state);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic inline void __cs_hsi_error_post(struct cs_hsi_iface *hi)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	spin_unlock(&hi->lock);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic inline void __cs_hsi_error_read_bits(unsigned int *state)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	*state |= SSI_CHANNEL_STATE_ERROR;
38262306a36Sopenharmony_ci	*state &= ~(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic inline void __cs_hsi_error_write_bits(unsigned int *state)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	*state |= SSI_CHANNEL_STATE_ERROR;
38862306a36Sopenharmony_ci	*state &= ~SSI_CHANNEL_STATE_WRITING;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void cs_hsi_control_read_error(struct cs_hsi_iface *hi,
39262306a36Sopenharmony_ci							struct hsi_msg *msg)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	__cs_hsi_error_pre(hi, msg, "control read", &hi->control_state);
39562306a36Sopenharmony_ci	cs_release_cmd(msg);
39662306a36Sopenharmony_ci	__cs_hsi_error_read_bits(&hi->control_state);
39762306a36Sopenharmony_ci	__cs_hsi_error_post(hi);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void cs_hsi_control_write_error(struct cs_hsi_iface *hi,
40162306a36Sopenharmony_ci							struct hsi_msg *msg)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	__cs_hsi_error_pre(hi, msg, "control write", &hi->control_state);
40462306a36Sopenharmony_ci	cs_release_cmd(msg);
40562306a36Sopenharmony_ci	__cs_hsi_error_write_bits(&hi->control_state);
40662306a36Sopenharmony_ci	__cs_hsi_error_post(hi);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic void cs_hsi_data_read_error(struct cs_hsi_iface *hi, struct hsi_msg *msg)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	__cs_hsi_error_pre(hi, msg, "data read", &hi->data_state);
41362306a36Sopenharmony_ci	__cs_hsi_error_read_bits(&hi->data_state);
41462306a36Sopenharmony_ci	__cs_hsi_error_post(hi);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void cs_hsi_data_write_error(struct cs_hsi_iface *hi,
41862306a36Sopenharmony_ci							struct hsi_msg *msg)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	__cs_hsi_error_pre(hi, msg, "data write", &hi->data_state);
42162306a36Sopenharmony_ci	__cs_hsi_error_write_bits(&hi->data_state);
42262306a36Sopenharmony_ci	__cs_hsi_error_post(hi);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	u32 cmd = cs_get_cmd(msg);
42862306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	spin_lock(&hi->lock);
43162306a36Sopenharmony_ci	hi->control_state &= ~SSI_CHANNEL_STATE_READING;
43262306a36Sopenharmony_ci	if (msg->status == HSI_STATUS_ERROR) {
43362306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Control RX error detected\n");
43462306a36Sopenharmony_ci		spin_unlock(&hi->lock);
43562306a36Sopenharmony_ci		cs_hsi_control_read_error(hi, msg);
43662306a36Sopenharmony_ci		goto out;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
43962306a36Sopenharmony_ci	cs_release_cmd(msg);
44062306a36Sopenharmony_ci	if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) {
44162306a36Sopenharmony_ci		struct timespec64 tspec;
44262306a36Sopenharmony_ci		struct cs_timestamp *tstamp =
44362306a36Sopenharmony_ci			&hi->mmap_cfg->tstamp_rx_ctrl;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ktime_get_ts64(&tspec);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		tstamp->tv_sec = (__u32) tspec.tv_sec;
44862306a36Sopenharmony_ci		tstamp->tv_nsec = (__u32) tspec.tv_nsec;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci	spin_unlock(&hi->lock);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	cs_notify_control(cmd);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ciout:
45562306a36Sopenharmony_ci	cs_hsi_read_on_control(hi);
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void cs_hsi_peek_on_control_complete(struct hsi_msg *msg)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
46162306a36Sopenharmony_ci	int ret;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (msg->status == HSI_STATUS_ERROR) {
46462306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Control peek RX error detected\n");
46562306a36Sopenharmony_ci		cs_hsi_control_read_error(hi, msg);
46662306a36Sopenharmony_ci		return;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	WARN_ON(!(hi->control_state & SSI_CHANNEL_STATE_READING));
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	dev_dbg(&hi->cl->device, "Peek on control complete, reading\n");
47262306a36Sopenharmony_ci	msg->sgt.nents = 1;
47362306a36Sopenharmony_ci	msg->complete = cs_hsi_read_on_control_complete;
47462306a36Sopenharmony_ci	ret = hsi_async_read(hi->cl, msg);
47562306a36Sopenharmony_ci	if (ret)
47662306a36Sopenharmony_ci		cs_hsi_control_read_error(hi, msg);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void cs_hsi_read_on_control(struct cs_hsi_iface *hi)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct hsi_msg *msg;
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	spin_lock(&hi->lock);
48562306a36Sopenharmony_ci	if (hi->control_state & SSI_CHANNEL_STATE_READING) {
48662306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Control read already pending (%d)\n",
48762306a36Sopenharmony_ci			hi->control_state);
48862306a36Sopenharmony_ci		spin_unlock(&hi->lock);
48962306a36Sopenharmony_ci		return;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci	if (hi->control_state & SSI_CHANNEL_STATE_ERROR) {
49262306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Control read error (%d)\n",
49362306a36Sopenharmony_ci			hi->control_state);
49462306a36Sopenharmony_ci		spin_unlock(&hi->lock);
49562306a36Sopenharmony_ci		return;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci	hi->control_state |= SSI_CHANNEL_STATE_READING;
49862306a36Sopenharmony_ci	dev_dbg(&hi->cl->device, "Issuing RX on control\n");
49962306a36Sopenharmony_ci	msg = cs_claim_cmd(hi);
50062306a36Sopenharmony_ci	spin_unlock(&hi->lock);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	msg->sgt.nents = 0;
50362306a36Sopenharmony_ci	msg->complete = cs_hsi_peek_on_control_complete;
50462306a36Sopenharmony_ci	ret = hsi_async_read(hi->cl, msg);
50562306a36Sopenharmony_ci	if (ret)
50662306a36Sopenharmony_ci		cs_hsi_control_read_error(hi, msg);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void cs_hsi_write_on_control_complete(struct hsi_msg *msg)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
51262306a36Sopenharmony_ci	if (msg->status == HSI_STATUS_COMPLETED) {
51362306a36Sopenharmony_ci		spin_lock(&hi->lock);
51462306a36Sopenharmony_ci		hi->control_state &= ~SSI_CHANNEL_STATE_WRITING;
51562306a36Sopenharmony_ci		cs_release_cmd(msg);
51662306a36Sopenharmony_ci		spin_unlock(&hi->lock);
51762306a36Sopenharmony_ci	} else if (msg->status == HSI_STATUS_ERROR) {
51862306a36Sopenharmony_ci		cs_hsi_control_write_error(hi, msg);
51962306a36Sopenharmony_ci	} else {
52062306a36Sopenharmony_ci		dev_err(&hi->cl->device,
52162306a36Sopenharmony_ci			"unexpected status in control write callback %d\n",
52262306a36Sopenharmony_ci			msg->status);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int cs_hsi_write_on_control(struct cs_hsi_iface *hi, u32 message)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct hsi_msg *msg;
52962306a36Sopenharmony_ci	int ret;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	spin_lock(&hi->lock);
53262306a36Sopenharmony_ci	if (hi->control_state & SSI_CHANNEL_STATE_ERROR) {
53362306a36Sopenharmony_ci		spin_unlock(&hi->lock);
53462306a36Sopenharmony_ci		return -EIO;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci	if (hi->control_state & SSI_CHANNEL_STATE_WRITING) {
53762306a36Sopenharmony_ci		dev_err(&hi->cl->device,
53862306a36Sopenharmony_ci			"Write still pending on control channel.\n");
53962306a36Sopenharmony_ci		spin_unlock(&hi->lock);
54062306a36Sopenharmony_ci		return -EBUSY;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci	hi->control_state |= SSI_CHANNEL_STATE_WRITING;
54362306a36Sopenharmony_ci	msg = cs_claim_cmd(hi);
54462306a36Sopenharmony_ci	spin_unlock(&hi->lock);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	cs_set_cmd(msg, message);
54762306a36Sopenharmony_ci	msg->sgt.nents = 1;
54862306a36Sopenharmony_ci	msg->complete = cs_hsi_write_on_control_complete;
54962306a36Sopenharmony_ci	dev_dbg(&hi->cl->device,
55062306a36Sopenharmony_ci		"Sending control message %08X\n", message);
55162306a36Sopenharmony_ci	ret = hsi_async_write(hi->cl, msg);
55262306a36Sopenharmony_ci	if (ret) {
55362306a36Sopenharmony_ci		dev_err(&hi->cl->device,
55462306a36Sopenharmony_ci			"async_write failed with %d\n", ret);
55562306a36Sopenharmony_ci		cs_hsi_control_write_error(hi, msg);
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/*
55962306a36Sopenharmony_ci	 * Make sure control read is always pending when issuing
56062306a36Sopenharmony_ci	 * new control writes. This is needed as the controller
56162306a36Sopenharmony_ci	 * may flush our messages if e.g. the peer device reboots
56262306a36Sopenharmony_ci	 * unexpectedly (and we cannot directly resubmit a new read from
56362306a36Sopenharmony_ci	 * the message destructor; see cs_cmd_destructor()).
56462306a36Sopenharmony_ci	 */
56562306a36Sopenharmony_ci	if (!(hi->control_state & SSI_CHANNEL_STATE_READING)) {
56662306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Restarting control reads\n");
56762306a36Sopenharmony_ci		cs_hsi_read_on_control(hi);
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void cs_hsi_read_on_data_complete(struct hsi_msg *msg)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
57662306a36Sopenharmony_ci	u32 payload;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (unlikely(msg->status == HSI_STATUS_ERROR)) {
57962306a36Sopenharmony_ci		cs_hsi_data_read_error(hi, msg);
58062306a36Sopenharmony_ci		return;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	spin_lock(&hi->lock);
58462306a36Sopenharmony_ci	WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_READING));
58562306a36Sopenharmony_ci	hi->data_state &= ~SSI_CHANNEL_STATE_READING;
58662306a36Sopenharmony_ci	payload = CS_RX_DATA_RECEIVED;
58762306a36Sopenharmony_ci	payload |= hi->rx_slot;
58862306a36Sopenharmony_ci	hi->rx_slot++;
58962306a36Sopenharmony_ci	hi->rx_slot %= hi->rx_ptr_boundary;
59062306a36Sopenharmony_ci	/* expose current rx ptr in mmap area */
59162306a36Sopenharmony_ci	hi->mmap_cfg->rx_ptr = hi->rx_slot;
59262306a36Sopenharmony_ci	if (unlikely(waitqueue_active(&hi->datawait)))
59362306a36Sopenharmony_ci		wake_up_interruptible(&hi->datawait);
59462306a36Sopenharmony_ci	spin_unlock(&hi->lock);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	cs_notify_data(payload, hi->rx_bufs);
59762306a36Sopenharmony_ci	cs_hsi_read_on_data(hi);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic void cs_hsi_peek_on_data_complete(struct hsi_msg *msg)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
60362306a36Sopenharmony_ci	u32 *address;
60462306a36Sopenharmony_ci	int ret;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (unlikely(msg->status == HSI_STATUS_ERROR)) {
60762306a36Sopenharmony_ci		cs_hsi_data_read_error(hi, msg);
60862306a36Sopenharmony_ci		return;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci	if (unlikely(hi->iface_state != CS_STATE_CONFIGURED)) {
61162306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Data received in invalid state\n");
61262306a36Sopenharmony_ci		cs_hsi_data_read_error(hi, msg);
61362306a36Sopenharmony_ci		return;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	spin_lock(&hi->lock);
61762306a36Sopenharmony_ci	WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_POLL));
61862306a36Sopenharmony_ci	hi->data_state &= ~SSI_CHANNEL_STATE_POLL;
61962306a36Sopenharmony_ci	hi->data_state |= SSI_CHANNEL_STATE_READING;
62062306a36Sopenharmony_ci	spin_unlock(&hi->lock);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	address = (u32 *)(hi->mmap_base +
62362306a36Sopenharmony_ci				hi->rx_offsets[hi->rx_slot % hi->rx_bufs]);
62462306a36Sopenharmony_ci	sg_init_one(msg->sgt.sgl, address, hi->buf_size);
62562306a36Sopenharmony_ci	msg->sgt.nents = 1;
62662306a36Sopenharmony_ci	msg->complete = cs_hsi_read_on_data_complete;
62762306a36Sopenharmony_ci	ret = hsi_async_read(hi->cl, msg);
62862306a36Sopenharmony_ci	if (ret)
62962306a36Sopenharmony_ci		cs_hsi_data_read_error(hi, msg);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci/*
63362306a36Sopenharmony_ci * Read/write transaction is ongoing. Returns false if in
63462306a36Sopenharmony_ci * SSI_CHANNEL_STATE_POLL state.
63562306a36Sopenharmony_ci */
63662306a36Sopenharmony_cistatic inline int cs_state_xfer_active(unsigned int state)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	return (state & SSI_CHANNEL_STATE_WRITING) ||
63962306a36Sopenharmony_ci		(state & SSI_CHANNEL_STATE_READING);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci/*
64362306a36Sopenharmony_ci * No pending read/writes
64462306a36Sopenharmony_ci */
64562306a36Sopenharmony_cistatic inline int cs_state_idle(unsigned int state)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	return !(state & ~SSI_CHANNEL_STATE_ERROR);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic void cs_hsi_read_on_data(struct cs_hsi_iface *hi)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct hsi_msg *rxmsg;
65362306a36Sopenharmony_ci	int ret;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	spin_lock(&hi->lock);
65662306a36Sopenharmony_ci	if (hi->data_state &
65762306a36Sopenharmony_ci		(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL)) {
65862306a36Sopenharmony_ci		dev_dbg(&hi->cl->device, "Data read already pending (%u)\n",
65962306a36Sopenharmony_ci			hi->data_state);
66062306a36Sopenharmony_ci		spin_unlock(&hi->lock);
66162306a36Sopenharmony_ci		return;
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci	hi->data_state |= SSI_CHANNEL_STATE_POLL;
66462306a36Sopenharmony_ci	spin_unlock(&hi->lock);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	rxmsg = hi->data_rx_msg;
66762306a36Sopenharmony_ci	sg_init_one(rxmsg->sgt.sgl, (void *)hi->mmap_base, 0);
66862306a36Sopenharmony_ci	rxmsg->sgt.nents = 0;
66962306a36Sopenharmony_ci	rxmsg->complete = cs_hsi_peek_on_data_complete;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	ret = hsi_async_read(hi->cl, rxmsg);
67262306a36Sopenharmony_ci	if (ret)
67362306a36Sopenharmony_ci		cs_hsi_data_read_error(hi, rxmsg);
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic void cs_hsi_write_on_data_complete(struct hsi_msg *msg)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct cs_hsi_iface *hi = msg->context;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (msg->status == HSI_STATUS_COMPLETED) {
68162306a36Sopenharmony_ci		spin_lock(&hi->lock);
68262306a36Sopenharmony_ci		hi->data_state &= ~SSI_CHANNEL_STATE_WRITING;
68362306a36Sopenharmony_ci		if (unlikely(waitqueue_active(&hi->datawait)))
68462306a36Sopenharmony_ci			wake_up_interruptible(&hi->datawait);
68562306a36Sopenharmony_ci		spin_unlock(&hi->lock);
68662306a36Sopenharmony_ci	} else {
68762306a36Sopenharmony_ci		cs_hsi_data_write_error(hi, msg);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int cs_hsi_write_on_data(struct cs_hsi_iface *hi, unsigned int slot)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	u32 *address;
69462306a36Sopenharmony_ci	struct hsi_msg *txmsg;
69562306a36Sopenharmony_ci	int ret;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	spin_lock(&hi->lock);
69862306a36Sopenharmony_ci	if (hi->iface_state != CS_STATE_CONFIGURED) {
69962306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Not configured, aborting\n");
70062306a36Sopenharmony_ci		ret = -EINVAL;
70162306a36Sopenharmony_ci		goto error;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	if (hi->data_state & SSI_CHANNEL_STATE_ERROR) {
70462306a36Sopenharmony_ci		dev_err(&hi->cl->device, "HSI error, aborting\n");
70562306a36Sopenharmony_ci		ret = -EIO;
70662306a36Sopenharmony_ci		goto error;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci	if (hi->data_state & SSI_CHANNEL_STATE_WRITING) {
70962306a36Sopenharmony_ci		dev_err(&hi->cl->device, "Write pending on data channel.\n");
71062306a36Sopenharmony_ci		ret = -EBUSY;
71162306a36Sopenharmony_ci		goto error;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	hi->data_state |= SSI_CHANNEL_STATE_WRITING;
71462306a36Sopenharmony_ci	spin_unlock(&hi->lock);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	hi->tx_slot = slot;
71762306a36Sopenharmony_ci	address = (u32 *)(hi->mmap_base + hi->tx_offsets[hi->tx_slot]);
71862306a36Sopenharmony_ci	txmsg = hi->data_tx_msg;
71962306a36Sopenharmony_ci	sg_init_one(txmsg->sgt.sgl, address, hi->buf_size);
72062306a36Sopenharmony_ci	txmsg->complete = cs_hsi_write_on_data_complete;
72162306a36Sopenharmony_ci	ret = hsi_async_write(hi->cl, txmsg);
72262306a36Sopenharmony_ci	if (ret)
72362306a36Sopenharmony_ci		cs_hsi_data_write_error(hi, txmsg);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	return ret;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cierror:
72862306a36Sopenharmony_ci	spin_unlock(&hi->lock);
72962306a36Sopenharmony_ci	if (ret == -EIO)
73062306a36Sopenharmony_ci		cs_hsi_data_write_error(hi, hi->data_tx_msg);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	return ret;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic unsigned int cs_hsi_get_state(struct cs_hsi_iface *hi)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	return hi->iface_state;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int cs_hsi_command(struct cs_hsi_iface *hi, u32 cmd)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	int ret = 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	local_bh_disable();
74562306a36Sopenharmony_ci	switch (cmd & TARGET_MASK) {
74662306a36Sopenharmony_ci	case TARGET_REMOTE:
74762306a36Sopenharmony_ci		ret = cs_hsi_write_on_control(hi, cmd);
74862306a36Sopenharmony_ci		break;
74962306a36Sopenharmony_ci	case TARGET_LOCAL:
75062306a36Sopenharmony_ci		if ((cmd & CS_CMD_MASK) == CS_TX_DATA_READY)
75162306a36Sopenharmony_ci			ret = cs_hsi_write_on_data(hi, cmd & CS_PARAM_MASK);
75262306a36Sopenharmony_ci		else
75362306a36Sopenharmony_ci			ret = -EINVAL;
75462306a36Sopenharmony_ci		break;
75562306a36Sopenharmony_ci	default:
75662306a36Sopenharmony_ci		ret = -EINVAL;
75762306a36Sopenharmony_ci		break;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci	local_bh_enable();
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	return ret;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic void cs_hsi_set_wakeline(struct cs_hsi_iface *hi, bool new_state)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	int change = 0;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	spin_lock_bh(&hi->lock);
76962306a36Sopenharmony_ci	if (hi->wakeline_state != new_state) {
77062306a36Sopenharmony_ci		hi->wakeline_state = new_state;
77162306a36Sopenharmony_ci		change = 1;
77262306a36Sopenharmony_ci		dev_dbg(&hi->cl->device, "setting wake line to %d (%p)\n",
77362306a36Sopenharmony_ci			new_state, hi->cl);
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci	spin_unlock_bh(&hi->lock);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (change) {
77862306a36Sopenharmony_ci		if (new_state)
77962306a36Sopenharmony_ci			ssip_slave_start_tx(hi->master);
78062306a36Sopenharmony_ci		else
78162306a36Sopenharmony_ci			ssip_slave_stop_tx(hi->master);
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	dev_dbg(&hi->cl->device, "wake line set to %d (%p)\n",
78562306a36Sopenharmony_ci		new_state, hi->cl);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void set_buffer_sizes(struct cs_hsi_iface *hi, int rx_bufs, int tx_bufs)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	hi->rx_bufs = rx_bufs;
79162306a36Sopenharmony_ci	hi->tx_bufs = tx_bufs;
79262306a36Sopenharmony_ci	hi->mmap_cfg->rx_bufs = rx_bufs;
79362306a36Sopenharmony_ci	hi->mmap_cfg->tx_bufs = tx_bufs;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (hi->flags & CS_FEAT_ROLLING_RX_COUNTER) {
79662306a36Sopenharmony_ci		/*
79762306a36Sopenharmony_ci		 * For more robust overrun detection, let the rx
79862306a36Sopenharmony_ci		 * pointer run in range 0..'boundary-1'. Boundary
79962306a36Sopenharmony_ci		 * is a multiple of rx_bufs, and limited in max size
80062306a36Sopenharmony_ci		 * by RX_PTR_MAX_SHIFT to allow for fast ptr-diff
80162306a36Sopenharmony_ci		 * calculation.
80262306a36Sopenharmony_ci		 */
80362306a36Sopenharmony_ci		hi->rx_ptr_boundary = (rx_bufs << RX_PTR_BOUNDARY_SHIFT);
80462306a36Sopenharmony_ci		hi->mmap_cfg->rx_ptr_boundary = hi->rx_ptr_boundary;
80562306a36Sopenharmony_ci	} else {
80662306a36Sopenharmony_ci		hi->rx_ptr_boundary = hi->rx_bufs;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic int check_buf_params(struct cs_hsi_iface *hi,
81162306a36Sopenharmony_ci					const struct cs_buffer_config *buf_cfg)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	size_t buf_size_aligned = L1_CACHE_ALIGN(buf_cfg->buf_size) *
81462306a36Sopenharmony_ci					(buf_cfg->rx_bufs + buf_cfg->tx_bufs);
81562306a36Sopenharmony_ci	size_t ctrl_size_aligned = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg));
81662306a36Sopenharmony_ci	int r = 0;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (buf_cfg->rx_bufs > CS_MAX_BUFFERS ||
81962306a36Sopenharmony_ci					buf_cfg->tx_bufs > CS_MAX_BUFFERS) {
82062306a36Sopenharmony_ci		r = -EINVAL;
82162306a36Sopenharmony_ci	} else if ((buf_size_aligned + ctrl_size_aligned) >= hi->mmap_size) {
82262306a36Sopenharmony_ci		dev_err(&hi->cl->device, "No space for the requested buffer "
82362306a36Sopenharmony_ci			"configuration\n");
82462306a36Sopenharmony_ci		r = -ENOBUFS;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	return r;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci/*
83162306a36Sopenharmony_ci * Block until pending data transfers have completed.
83262306a36Sopenharmony_ci */
83362306a36Sopenharmony_cistatic int cs_hsi_data_sync(struct cs_hsi_iface *hi)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	int r = 0;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	spin_lock_bh(&hi->lock);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (!cs_state_xfer_active(hi->data_state)) {
84062306a36Sopenharmony_ci		dev_dbg(&hi->cl->device, "hsi_data_sync break, idle\n");
84162306a36Sopenharmony_ci		goto out;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	for (;;) {
84562306a36Sopenharmony_ci		int s;
84662306a36Sopenharmony_ci		DEFINE_WAIT(wait);
84762306a36Sopenharmony_ci		if (!cs_state_xfer_active(hi->data_state))
84862306a36Sopenharmony_ci			goto out;
84962306a36Sopenharmony_ci		if (signal_pending(current)) {
85062306a36Sopenharmony_ci			r = -ERESTARTSYS;
85162306a36Sopenharmony_ci			goto out;
85262306a36Sopenharmony_ci		}
85362306a36Sopenharmony_ci		/*
85462306a36Sopenharmony_ci		 * prepare_to_wait must be called with hi->lock held
85562306a36Sopenharmony_ci		 * so that callbacks can check for waitqueue_active()
85662306a36Sopenharmony_ci		 */
85762306a36Sopenharmony_ci		prepare_to_wait(&hi->datawait, &wait, TASK_INTERRUPTIBLE);
85862306a36Sopenharmony_ci		spin_unlock_bh(&hi->lock);
85962306a36Sopenharmony_ci		s = schedule_timeout(
86062306a36Sopenharmony_ci			msecs_to_jiffies(CS_HSI_TRANSFER_TIMEOUT_MS));
86162306a36Sopenharmony_ci		spin_lock_bh(&hi->lock);
86262306a36Sopenharmony_ci		finish_wait(&hi->datawait, &wait);
86362306a36Sopenharmony_ci		if (!s) {
86462306a36Sopenharmony_ci			dev_dbg(&hi->cl->device,
86562306a36Sopenharmony_ci				"hsi_data_sync timeout after %d ms\n",
86662306a36Sopenharmony_ci				CS_HSI_TRANSFER_TIMEOUT_MS);
86762306a36Sopenharmony_ci			r = -EIO;
86862306a36Sopenharmony_ci			goto out;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ciout:
87362306a36Sopenharmony_ci	spin_unlock_bh(&hi->lock);
87462306a36Sopenharmony_ci	dev_dbg(&hi->cl->device, "hsi_data_sync done with res %d\n", r);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return r;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic void cs_hsi_data_enable(struct cs_hsi_iface *hi,
88062306a36Sopenharmony_ci					struct cs_buffer_config *buf_cfg)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	unsigned int data_start, i;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	BUG_ON(hi->buf_size == 0);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	set_buffer_sizes(hi, buf_cfg->rx_bufs, buf_cfg->tx_bufs);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	hi->slot_size = L1_CACHE_ALIGN(hi->buf_size);
88962306a36Sopenharmony_ci	dev_dbg(&hi->cl->device,
89062306a36Sopenharmony_ci			"setting slot size to %u, buf size %u, align %u\n",
89162306a36Sopenharmony_ci			hi->slot_size, hi->buf_size, L1_CACHE_BYTES);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	data_start = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg));
89462306a36Sopenharmony_ci	dev_dbg(&hi->cl->device,
89562306a36Sopenharmony_ci			"setting data start at %u, cfg block %u, align %u\n",
89662306a36Sopenharmony_ci			data_start, sizeof(*hi->mmap_cfg), L1_CACHE_BYTES);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	for (i = 0; i < hi->mmap_cfg->rx_bufs; i++) {
89962306a36Sopenharmony_ci		hi->rx_offsets[i] = data_start + i * hi->slot_size;
90062306a36Sopenharmony_ci		hi->mmap_cfg->rx_offsets[i] = hi->rx_offsets[i];
90162306a36Sopenharmony_ci		dev_dbg(&hi->cl->device, "DL buf #%u at %u\n",
90262306a36Sopenharmony_ci					i, hi->rx_offsets[i]);
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	for (i = 0; i < hi->mmap_cfg->tx_bufs; i++) {
90562306a36Sopenharmony_ci		hi->tx_offsets[i] = data_start +
90662306a36Sopenharmony_ci			(i + hi->mmap_cfg->rx_bufs) * hi->slot_size;
90762306a36Sopenharmony_ci		hi->mmap_cfg->tx_offsets[i] = hi->tx_offsets[i];
90862306a36Sopenharmony_ci		dev_dbg(&hi->cl->device, "UL buf #%u at %u\n",
90962306a36Sopenharmony_ci					i, hi->rx_offsets[i]);
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	hi->iface_state = CS_STATE_CONFIGURED;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic void cs_hsi_data_disable(struct cs_hsi_iface *hi, int old_state)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	if (old_state == CS_STATE_CONFIGURED) {
91862306a36Sopenharmony_ci		dev_dbg(&hi->cl->device,
91962306a36Sopenharmony_ci			"closing data channel with slot size 0\n");
92062306a36Sopenharmony_ci		hi->iface_state = CS_STATE_OPENED;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic int cs_hsi_buf_config(struct cs_hsi_iface *hi,
92562306a36Sopenharmony_ci					struct cs_buffer_config *buf_cfg)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	int r = 0;
92862306a36Sopenharmony_ci	unsigned int old_state = hi->iface_state;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	spin_lock_bh(&hi->lock);
93162306a36Sopenharmony_ci	/* Prevent new transactions during buffer reconfig */
93262306a36Sopenharmony_ci	if (old_state == CS_STATE_CONFIGURED)
93362306a36Sopenharmony_ci		hi->iface_state = CS_STATE_OPENED;
93462306a36Sopenharmony_ci	spin_unlock_bh(&hi->lock);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/*
93762306a36Sopenharmony_ci	 * make sure that no non-zero data reads are ongoing before
93862306a36Sopenharmony_ci	 * proceeding to change the buffer layout
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_ci	r = cs_hsi_data_sync(hi);
94162306a36Sopenharmony_ci	if (r < 0)
94262306a36Sopenharmony_ci		return r;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	WARN_ON(cs_state_xfer_active(hi->data_state));
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	spin_lock_bh(&hi->lock);
94762306a36Sopenharmony_ci	r = check_buf_params(hi, buf_cfg);
94862306a36Sopenharmony_ci	if (r < 0)
94962306a36Sopenharmony_ci		goto error;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	hi->buf_size = buf_cfg->buf_size;
95262306a36Sopenharmony_ci	hi->mmap_cfg->buf_size = hi->buf_size;
95362306a36Sopenharmony_ci	hi->flags = buf_cfg->flags;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	hi->rx_slot = 0;
95662306a36Sopenharmony_ci	hi->tx_slot = 0;
95762306a36Sopenharmony_ci	hi->slot_size = 0;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (hi->buf_size)
96062306a36Sopenharmony_ci		cs_hsi_data_enable(hi, buf_cfg);
96162306a36Sopenharmony_ci	else
96262306a36Sopenharmony_ci		cs_hsi_data_disable(hi, old_state);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	spin_unlock_bh(&hi->lock);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (old_state != hi->iface_state) {
96762306a36Sopenharmony_ci		if (hi->iface_state == CS_STATE_CONFIGURED) {
96862306a36Sopenharmony_ci			cpu_latency_qos_add_request(&hi->pm_qos_req,
96962306a36Sopenharmony_ci				CS_QOS_LATENCY_FOR_DATA_USEC);
97062306a36Sopenharmony_ci			local_bh_disable();
97162306a36Sopenharmony_ci			cs_hsi_read_on_data(hi);
97262306a36Sopenharmony_ci			local_bh_enable();
97362306a36Sopenharmony_ci		} else if (old_state == CS_STATE_CONFIGURED) {
97462306a36Sopenharmony_ci			cpu_latency_qos_remove_request(&hi->pm_qos_req);
97562306a36Sopenharmony_ci		}
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci	return r;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cierror:
98062306a36Sopenharmony_ci	spin_unlock_bh(&hi->lock);
98162306a36Sopenharmony_ci	return r;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int cs_hsi_start(struct cs_hsi_iface **hi, struct hsi_client *cl,
98562306a36Sopenharmony_ci			unsigned long mmap_base, unsigned long mmap_size)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	int err = 0;
98862306a36Sopenharmony_ci	struct cs_hsi_iface *hsi_if = kzalloc(sizeof(*hsi_if), GFP_KERNEL);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	dev_dbg(&cl->device, "cs_hsi_start\n");
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (!hsi_if) {
99362306a36Sopenharmony_ci		err = -ENOMEM;
99462306a36Sopenharmony_ci		goto leave0;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci	spin_lock_init(&hsi_if->lock);
99762306a36Sopenharmony_ci	hsi_if->cl = cl;
99862306a36Sopenharmony_ci	hsi_if->iface_state = CS_STATE_CLOSED;
99962306a36Sopenharmony_ci	hsi_if->mmap_cfg = (struct cs_mmap_config_block *)mmap_base;
100062306a36Sopenharmony_ci	hsi_if->mmap_base = mmap_base;
100162306a36Sopenharmony_ci	hsi_if->mmap_size = mmap_size;
100262306a36Sopenharmony_ci	memset(hsi_if->mmap_cfg, 0, sizeof(*hsi_if->mmap_cfg));
100362306a36Sopenharmony_ci	init_waitqueue_head(&hsi_if->datawait);
100462306a36Sopenharmony_ci	err = cs_alloc_cmds(hsi_if);
100562306a36Sopenharmony_ci	if (err < 0) {
100662306a36Sopenharmony_ci		dev_err(&cl->device, "Unable to alloc HSI messages\n");
100762306a36Sopenharmony_ci		goto leave1;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci	err = cs_hsi_alloc_data(hsi_if);
101062306a36Sopenharmony_ci	if (err < 0) {
101162306a36Sopenharmony_ci		dev_err(&cl->device, "Unable to alloc HSI messages for data\n");
101262306a36Sopenharmony_ci		goto leave2;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci	err = hsi_claim_port(cl, 1);
101562306a36Sopenharmony_ci	if (err < 0) {
101662306a36Sopenharmony_ci		dev_err(&cl->device,
101762306a36Sopenharmony_ci				"Could not open, HSI port already claimed\n");
101862306a36Sopenharmony_ci		goto leave3;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci	hsi_if->master = ssip_slave_get_master(cl);
102162306a36Sopenharmony_ci	if (IS_ERR(hsi_if->master)) {
102262306a36Sopenharmony_ci		err = PTR_ERR(hsi_if->master);
102362306a36Sopenharmony_ci		dev_err(&cl->device, "Could not get HSI master client\n");
102462306a36Sopenharmony_ci		goto leave4;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci	if (!ssip_slave_running(hsi_if->master)) {
102762306a36Sopenharmony_ci		err = -ENODEV;
102862306a36Sopenharmony_ci		dev_err(&cl->device,
102962306a36Sopenharmony_ci				"HSI port not initialized\n");
103062306a36Sopenharmony_ci		goto leave4;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	hsi_if->iface_state = CS_STATE_OPENED;
103462306a36Sopenharmony_ci	local_bh_disable();
103562306a36Sopenharmony_ci	cs_hsi_read_on_control(hsi_if);
103662306a36Sopenharmony_ci	local_bh_enable();
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	dev_dbg(&cl->device, "cs_hsi_start...done\n");
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	BUG_ON(!hi);
104162306a36Sopenharmony_ci	*hi = hsi_if;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	return 0;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cileave4:
104662306a36Sopenharmony_ci	hsi_release_port(cl);
104762306a36Sopenharmony_cileave3:
104862306a36Sopenharmony_ci	cs_hsi_free_data(hsi_if);
104962306a36Sopenharmony_cileave2:
105062306a36Sopenharmony_ci	cs_free_cmds(hsi_if);
105162306a36Sopenharmony_cileave1:
105262306a36Sopenharmony_ci	kfree(hsi_if);
105362306a36Sopenharmony_cileave0:
105462306a36Sopenharmony_ci	dev_dbg(&cl->device, "cs_hsi_start...done/error\n\n");
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return err;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic void cs_hsi_stop(struct cs_hsi_iface *hi)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	dev_dbg(&hi->cl->device, "cs_hsi_stop\n");
106262306a36Sopenharmony_ci	cs_hsi_set_wakeline(hi, 0);
106362306a36Sopenharmony_ci	ssip_slave_put_master(hi->master);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* hsi_release_port() needs to be called with CS_STATE_CLOSED */
106662306a36Sopenharmony_ci	hi->iface_state = CS_STATE_CLOSED;
106762306a36Sopenharmony_ci	hsi_release_port(hi->cl);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/*
107062306a36Sopenharmony_ci	 * hsi_release_port() should flush out all the pending
107162306a36Sopenharmony_ci	 * messages, so cs_state_idle() should be true for both
107262306a36Sopenharmony_ci	 * control and data channels.
107362306a36Sopenharmony_ci	 */
107462306a36Sopenharmony_ci	WARN_ON(!cs_state_idle(hi->control_state));
107562306a36Sopenharmony_ci	WARN_ON(!cs_state_idle(hi->data_state));
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (cpu_latency_qos_request_active(&hi->pm_qos_req))
107862306a36Sopenharmony_ci		cpu_latency_qos_remove_request(&hi->pm_qos_req);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	spin_lock_bh(&hi->lock);
108162306a36Sopenharmony_ci	cs_hsi_free_data(hi);
108262306a36Sopenharmony_ci	cs_free_cmds(hi);
108362306a36Sopenharmony_ci	spin_unlock_bh(&hi->lock);
108462306a36Sopenharmony_ci	kfree(hi);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic vm_fault_t cs_char_vma_fault(struct vm_fault *vmf)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct cs_char *csdata = vmf->vma->vm_private_data;
109062306a36Sopenharmony_ci	struct page *page;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	page = virt_to_page((void *)csdata->mmap_base);
109362306a36Sopenharmony_ci	get_page(page);
109462306a36Sopenharmony_ci	vmf->page = page;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	return 0;
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic const struct vm_operations_struct cs_char_vm_ops = {
110062306a36Sopenharmony_ci	.fault	= cs_char_vma_fault,
110162306a36Sopenharmony_ci};
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic int cs_char_fasync(int fd, struct file *file, int on)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct cs_char *csdata = file->private_data;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	if (fasync_helper(fd, file, on, &csdata->async_queue) < 0)
110862306a36Sopenharmony_ci		return -EIO;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	return 0;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic __poll_t cs_char_poll(struct file *file, poll_table *wait)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	struct cs_char *csdata = file->private_data;
111662306a36Sopenharmony_ci	__poll_t ret = 0;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	poll_wait(file, &cs_char_data.wait, wait);
111962306a36Sopenharmony_ci	spin_lock_bh(&csdata->lock);
112062306a36Sopenharmony_ci	if (!list_empty(&csdata->chardev_queue))
112162306a36Sopenharmony_ci		ret = EPOLLIN | EPOLLRDNORM;
112262306a36Sopenharmony_ci	else if (!list_empty(&csdata->dataind_queue))
112362306a36Sopenharmony_ci		ret = EPOLLIN | EPOLLRDNORM;
112462306a36Sopenharmony_ci	spin_unlock_bh(&csdata->lock);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	return ret;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic ssize_t cs_char_read(struct file *file, char __user *buf, size_t count,
113062306a36Sopenharmony_ci								loff_t *unused)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct cs_char *csdata = file->private_data;
113362306a36Sopenharmony_ci	u32 data;
113462306a36Sopenharmony_ci	ssize_t retval;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (count < sizeof(data))
113762306a36Sopenharmony_ci		return -EINVAL;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	for (;;) {
114062306a36Sopenharmony_ci		DEFINE_WAIT(wait);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci		spin_lock_bh(&csdata->lock);
114362306a36Sopenharmony_ci		if (!list_empty(&csdata->chardev_queue)) {
114462306a36Sopenharmony_ci			data = cs_pop_entry(&csdata->chardev_queue);
114562306a36Sopenharmony_ci		} else if (!list_empty(&csdata->dataind_queue)) {
114662306a36Sopenharmony_ci			data = cs_pop_entry(&csdata->dataind_queue);
114762306a36Sopenharmony_ci			csdata->dataind_pending--;
114862306a36Sopenharmony_ci		} else {
114962306a36Sopenharmony_ci			data = 0;
115062306a36Sopenharmony_ci		}
115162306a36Sopenharmony_ci		spin_unlock_bh(&csdata->lock);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		if (data)
115462306a36Sopenharmony_ci			break;
115562306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
115662306a36Sopenharmony_ci			retval = -EAGAIN;
115762306a36Sopenharmony_ci			goto out;
115862306a36Sopenharmony_ci		} else if (signal_pending(current)) {
115962306a36Sopenharmony_ci			retval = -ERESTARTSYS;
116062306a36Sopenharmony_ci			goto out;
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci		prepare_to_wait_exclusive(&csdata->wait, &wait,
116362306a36Sopenharmony_ci						TASK_INTERRUPTIBLE);
116462306a36Sopenharmony_ci		schedule();
116562306a36Sopenharmony_ci		finish_wait(&csdata->wait, &wait);
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	retval = put_user(data, (u32 __user *)buf);
116962306a36Sopenharmony_ci	if (!retval)
117062306a36Sopenharmony_ci		retval = sizeof(data);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ciout:
117362306a36Sopenharmony_ci	return retval;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cistatic ssize_t cs_char_write(struct file *file, const char __user *buf,
117762306a36Sopenharmony_ci						size_t count, loff_t *unused)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct cs_char *csdata = file->private_data;
118062306a36Sopenharmony_ci	u32 data;
118162306a36Sopenharmony_ci	int err;
118262306a36Sopenharmony_ci	ssize_t	retval;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (count < sizeof(data))
118562306a36Sopenharmony_ci		return -EINVAL;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (get_user(data, (u32 __user *)buf))
118862306a36Sopenharmony_ci		retval = -EFAULT;
118962306a36Sopenharmony_ci	else
119062306a36Sopenharmony_ci		retval = count;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	err = cs_hsi_command(csdata->hi, data);
119362306a36Sopenharmony_ci	if (err < 0)
119462306a36Sopenharmony_ci		retval = err;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return retval;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic long cs_char_ioctl(struct file *file, unsigned int cmd,
120062306a36Sopenharmony_ci				unsigned long arg)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct cs_char *csdata = file->private_data;
120362306a36Sopenharmony_ci	int r = 0;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	switch (cmd) {
120662306a36Sopenharmony_ci	case CS_GET_STATE: {
120762306a36Sopenharmony_ci		unsigned int state;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		state = cs_hsi_get_state(csdata->hi);
121062306a36Sopenharmony_ci		if (copy_to_user((void __user *)arg, &state, sizeof(state)))
121162306a36Sopenharmony_ci			r = -EFAULT;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		break;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci	case CS_SET_WAKELINE: {
121662306a36Sopenharmony_ci		unsigned int state;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		if (copy_from_user(&state, (void __user *)arg, sizeof(state))) {
121962306a36Sopenharmony_ci			r = -EFAULT;
122062306a36Sopenharmony_ci			break;
122162306a36Sopenharmony_ci		}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		if (state > 1) {
122462306a36Sopenharmony_ci			r = -EINVAL;
122562306a36Sopenharmony_ci			break;
122662306a36Sopenharmony_ci		}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci		cs_hsi_set_wakeline(csdata->hi, !!state);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		break;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci	case CS_GET_IF_VERSION: {
123362306a36Sopenharmony_ci		unsigned int ifver = CS_IF_VERSION;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci		if (copy_to_user((void __user *)arg, &ifver, sizeof(ifver)))
123662306a36Sopenharmony_ci			r = -EFAULT;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci		break;
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci	case CS_CONFIG_BUFS: {
124162306a36Sopenharmony_ci		struct cs_buffer_config buf_cfg;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci		if (copy_from_user(&buf_cfg, (void __user *)arg,
124462306a36Sopenharmony_ci							sizeof(buf_cfg)))
124562306a36Sopenharmony_ci			r = -EFAULT;
124662306a36Sopenharmony_ci		else
124762306a36Sopenharmony_ci			r = cs_hsi_buf_config(csdata->hi, &buf_cfg);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci	default:
125262306a36Sopenharmony_ci		r = -ENOTTY;
125362306a36Sopenharmony_ci		break;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	return r;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic int cs_char_mmap(struct file *file, struct vm_area_struct *vma)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	if (vma->vm_end < vma->vm_start)
126262306a36Sopenharmony_ci		return -EINVAL;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (vma_pages(vma) != 1)
126562306a36Sopenharmony_ci		return -EINVAL;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	vm_flags_set(vma, VM_IO | VM_DONTDUMP | VM_DONTEXPAND);
126862306a36Sopenharmony_ci	vma->vm_ops = &cs_char_vm_ops;
126962306a36Sopenharmony_ci	vma->vm_private_data = file->private_data;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return 0;
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int cs_char_open(struct inode *unused, struct file *file)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	int ret = 0;
127762306a36Sopenharmony_ci	unsigned long p;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	spin_lock_bh(&cs_char_data.lock);
128062306a36Sopenharmony_ci	if (cs_char_data.opened) {
128162306a36Sopenharmony_ci		ret = -EBUSY;
128262306a36Sopenharmony_ci		spin_unlock_bh(&cs_char_data.lock);
128362306a36Sopenharmony_ci		goto out1;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci	cs_char_data.opened = 1;
128662306a36Sopenharmony_ci	cs_char_data.dataind_pending = 0;
128762306a36Sopenharmony_ci	spin_unlock_bh(&cs_char_data.lock);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	p = get_zeroed_page(GFP_KERNEL);
129062306a36Sopenharmony_ci	if (!p) {
129162306a36Sopenharmony_ci		ret = -ENOMEM;
129262306a36Sopenharmony_ci		goto out2;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	ret = cs_hsi_start(&cs_char_data.hi, cs_char_data.cl, p, CS_MMAP_SIZE);
129662306a36Sopenharmony_ci	if (ret) {
129762306a36Sopenharmony_ci		dev_err(&cs_char_data.cl->device, "Unable to initialize HSI\n");
129862306a36Sopenharmony_ci		goto out3;
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	/* these are only used in release so lock not needed */
130262306a36Sopenharmony_ci	cs_char_data.mmap_base = p;
130362306a36Sopenharmony_ci	cs_char_data.mmap_size = CS_MMAP_SIZE;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	file->private_data = &cs_char_data;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	return 0;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ciout3:
131062306a36Sopenharmony_ci	free_page(p);
131162306a36Sopenharmony_ciout2:
131262306a36Sopenharmony_ci	spin_lock_bh(&cs_char_data.lock);
131362306a36Sopenharmony_ci	cs_char_data.opened = 0;
131462306a36Sopenharmony_ci	spin_unlock_bh(&cs_char_data.lock);
131562306a36Sopenharmony_ciout1:
131662306a36Sopenharmony_ci	return ret;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic void cs_free_char_queue(struct list_head *head)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct char_queue *entry;
132262306a36Sopenharmony_ci	struct list_head *cursor, *next;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (!list_empty(head)) {
132562306a36Sopenharmony_ci		list_for_each_safe(cursor, next, head) {
132662306a36Sopenharmony_ci			entry = list_entry(cursor, struct char_queue, list);
132762306a36Sopenharmony_ci			list_del(&entry->list);
132862306a36Sopenharmony_ci			kfree(entry);
132962306a36Sopenharmony_ci		}
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic int cs_char_release(struct inode *unused, struct file *file)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct cs_char *csdata = file->private_data;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	cs_hsi_stop(csdata->hi);
133962306a36Sopenharmony_ci	spin_lock_bh(&csdata->lock);
134062306a36Sopenharmony_ci	csdata->hi = NULL;
134162306a36Sopenharmony_ci	free_page(csdata->mmap_base);
134262306a36Sopenharmony_ci	cs_free_char_queue(&csdata->chardev_queue);
134362306a36Sopenharmony_ci	cs_free_char_queue(&csdata->dataind_queue);
134462306a36Sopenharmony_ci	csdata->opened = 0;
134562306a36Sopenharmony_ci	spin_unlock_bh(&csdata->lock);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	return 0;
134862306a36Sopenharmony_ci}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_cistatic const struct file_operations cs_char_fops = {
135162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
135262306a36Sopenharmony_ci	.read		= cs_char_read,
135362306a36Sopenharmony_ci	.write		= cs_char_write,
135462306a36Sopenharmony_ci	.poll		= cs_char_poll,
135562306a36Sopenharmony_ci	.unlocked_ioctl	= cs_char_ioctl,
135662306a36Sopenharmony_ci	.mmap		= cs_char_mmap,
135762306a36Sopenharmony_ci	.open		= cs_char_open,
135862306a36Sopenharmony_ci	.release	= cs_char_release,
135962306a36Sopenharmony_ci	.fasync		= cs_char_fasync,
136062306a36Sopenharmony_ci};
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_cistatic struct miscdevice cs_char_miscdev = {
136362306a36Sopenharmony_ci	.minor	= MISC_DYNAMIC_MINOR,
136462306a36Sopenharmony_ci	.name	= "cmt_speech",
136562306a36Sopenharmony_ci	.fops	= &cs_char_fops
136662306a36Sopenharmony_ci};
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_cistatic int cs_hsi_client_probe(struct device *dev)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	int err = 0;
137162306a36Sopenharmony_ci	struct hsi_client *cl = to_hsi_client(dev);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	dev_dbg(dev, "hsi_client_probe\n");
137462306a36Sopenharmony_ci	init_waitqueue_head(&cs_char_data.wait);
137562306a36Sopenharmony_ci	spin_lock_init(&cs_char_data.lock);
137662306a36Sopenharmony_ci	cs_char_data.opened = 0;
137762306a36Sopenharmony_ci	cs_char_data.cl = cl;
137862306a36Sopenharmony_ci	cs_char_data.hi = NULL;
137962306a36Sopenharmony_ci	INIT_LIST_HEAD(&cs_char_data.chardev_queue);
138062306a36Sopenharmony_ci	INIT_LIST_HEAD(&cs_char_data.dataind_queue);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	cs_char_data.channel_id_cmd = hsi_get_channel_id_by_name(cl,
138362306a36Sopenharmony_ci		"speech-control");
138462306a36Sopenharmony_ci	if (cs_char_data.channel_id_cmd < 0) {
138562306a36Sopenharmony_ci		err = cs_char_data.channel_id_cmd;
138662306a36Sopenharmony_ci		dev_err(dev, "Could not get cmd channel (%d)\n", err);
138762306a36Sopenharmony_ci		return err;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	cs_char_data.channel_id_data = hsi_get_channel_id_by_name(cl,
139162306a36Sopenharmony_ci		"speech-data");
139262306a36Sopenharmony_ci	if (cs_char_data.channel_id_data < 0) {
139362306a36Sopenharmony_ci		err = cs_char_data.channel_id_data;
139462306a36Sopenharmony_ci		dev_err(dev, "Could not get data channel (%d)\n", err);
139562306a36Sopenharmony_ci		return err;
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	err = misc_register(&cs_char_miscdev);
139962306a36Sopenharmony_ci	if (err)
140062306a36Sopenharmony_ci		dev_err(dev, "Failed to register: %d\n", err);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	return err;
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic int cs_hsi_client_remove(struct device *dev)
140662306a36Sopenharmony_ci{
140762306a36Sopenharmony_ci	struct cs_hsi_iface *hi;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	dev_dbg(dev, "hsi_client_remove\n");
141062306a36Sopenharmony_ci	misc_deregister(&cs_char_miscdev);
141162306a36Sopenharmony_ci	spin_lock_bh(&cs_char_data.lock);
141262306a36Sopenharmony_ci	hi = cs_char_data.hi;
141362306a36Sopenharmony_ci	cs_char_data.hi = NULL;
141462306a36Sopenharmony_ci	spin_unlock_bh(&cs_char_data.lock);
141562306a36Sopenharmony_ci	if (hi)
141662306a36Sopenharmony_ci		cs_hsi_stop(hi);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	return 0;
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_cistatic struct hsi_client_driver cs_hsi_driver = {
142262306a36Sopenharmony_ci	.driver = {
142362306a36Sopenharmony_ci		.name	= "cmt-speech",
142462306a36Sopenharmony_ci		.owner	= THIS_MODULE,
142562306a36Sopenharmony_ci		.probe	= cs_hsi_client_probe,
142662306a36Sopenharmony_ci		.remove	= cs_hsi_client_remove,
142762306a36Sopenharmony_ci	},
142862306a36Sopenharmony_ci};
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic int __init cs_char_init(void)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	pr_info("CMT speech driver added\n");
143362306a36Sopenharmony_ci	return hsi_register_client_driver(&cs_hsi_driver);
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_cimodule_init(cs_char_init);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_cistatic void __exit cs_char_exit(void)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	hsi_unregister_client_driver(&cs_hsi_driver);
144062306a36Sopenharmony_ci	pr_info("CMT speech driver removed\n");
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_cimodule_exit(cs_char_exit);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ciMODULE_ALIAS("hsi:cmt-speech");
144562306a36Sopenharmony_ciMODULE_AUTHOR("Kai Vehmanen <kai.vehmanen@nokia.com>");
144662306a36Sopenharmony_ciMODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
144762306a36Sopenharmony_ciMODULE_DESCRIPTION("CMT speech driver");
144862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1449