blob: 79ff26aef65a438af1fdbf2aee08d04a07f6a49e [file] [log] [blame]
Vidyakumar Athota7206c722017-12-05 11:43:47 -08001/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
Laxminath Kasamd1f36192017-08-03 18:54:01 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
Vidyakumar Athota7206c722017-12-05 11:43:47 -080016#include <linux/spinlock.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053017#include <linux/errno.h>
18#include <linux/fs.h>
19#include <linux/uaccess.h>
20#include <linux/slab.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053021#include <linux/cdev.h>
22#include <linux/platform_device.h>
Vidyakumar Athota7206c722017-12-05 11:43:47 -080023#include <linux/of_device.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053024#include <linux/vmalloc.h>
Vidyakumar Athota7206c722017-12-05 11:43:47 -080025#include <linux/rpmsg.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053026#include "sound/wcd-dsp-glink.h"
27
28#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink"
29#define WDSP_MAX_WRITE_SIZE (256 * 1024)
30#define WDSP_MAX_READ_SIZE (4 * 1024)
Laxminath Kasamd1f36192017-08-03 18:54:01 +053031#define WDSP_WRITE_PKT_SIZE (sizeof(struct wdsp_write_pkt))
Laxminath Kasamd1f36192017-08-03 18:54:01 +053032#define WDSP_CMD_PKT_SIZE (sizeof(struct wdsp_cmd_pkt))
Laxminath Kasamd1f36192017-08-03 18:54:01 +053033
34#define MINOR_NUMBER_COUNT 1
Laxminath Kasamd1f36192017-08-03 18:54:01 +053035#define RESP_QUEUE_SIZE 3
Vidyakumar Athota71699fd2018-10-16 11:32:47 -070036#define TIMEOUT_MS 2000
Laxminath Kasamd1f36192017-08-03 18:54:01 +053037
Vidyakumar Athota7206c722017-12-05 11:43:47 -080038enum wdsp_ch_state {
39 WDSP_CH_DISCONNECTED,
40 WDSP_CH_CONNECTED,
41};
42
Laxminath Kasamd1f36192017-08-03 18:54:01 +053043struct wdsp_glink_dev {
44 struct class *cls;
45 struct device *dev;
46 struct cdev cdev;
47 dev_t dev_num;
48};
49
Vidyakumar Athota7206c722017-12-05 11:43:47 -080050struct wdsp_rsp_que {
Laxminath Kasamd1f36192017-08-03 18:54:01 +053051 /* Size of valid data in buffer */
52 u32 buf_size;
53
54 /* Response buffer */
55 u8 buf[WDSP_MAX_READ_SIZE];
56};
57
Vidyakumar Athota7206c722017-12-05 11:43:47 -080058struct wdsp_ch {
59 struct wdsp_glink_priv *wpriv;
60 /* rpmsg handle */
61 void *handle;
62 /* Channel states like connect, disconnect */
63 int ch_state;
64 char ch_name[RPMSG_NAME_SIZE];
65 spinlock_t ch_lock;
66};
67
68struct wdsp_tx_buf {
Laxminath Kasamd1f36192017-08-03 18:54:01 +053069 struct work_struct tx_work;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053070
71 /* Glink channel information */
Vidyakumar Athota7206c722017-12-05 11:43:47 -080072 struct wdsp_ch *ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053073
74 /* Tx buffer to send to glink */
75 u8 buf[0];
76};
77
Laxminath Kasamd1f36192017-08-03 18:54:01 +053078struct wdsp_glink_priv {
79 /* Respone buffer related */
80 u8 rsp_cnt;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080081 struct wdsp_rsp_que rsp[RESP_QUEUE_SIZE];
Xiaojun Sang5e8453b2018-06-15 17:03:42 +080082 u8 write_idx;
83 u8 read_idx;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053084 struct completion rsp_complete;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080085 spinlock_t rsp_lock;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053086
87 /* Glink channel related */
Vidyakumar Athota7206c722017-12-05 11:43:47 -080088 int no_of_channels;
89 struct wdsp_ch **ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053090 struct workqueue_struct *work_queue;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080091 /* Wait for all channels state before sending any command */
92 wait_queue_head_t ch_state_wait;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053093
Vidyakumar Athota7206c722017-12-05 11:43:47 -080094 struct wdsp_glink_dev *wdev;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053095 struct device *dev;
96};
Vidyakumar Athota7206c722017-12-05 11:43:47 -080097static struct wdsp_glink_priv *wpriv;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053098
Vidyakumar Athota7206c722017-12-05 11:43:47 -080099static struct wdsp_ch *wdsp_get_ch(char *ch_name)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530100{
101 int i;
102
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530103 for (i = 0; i < wpriv->no_of_channels; i++) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800104 if (!strcmp(ch_name, wpriv->ch[i]->ch_name))
105 return wpriv->ch[i];
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530106 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800107 return NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530108}
109
110/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800111 * wdsp_rpmsg_callback - Rpmsg callback for responses
112 * rpdev: Rpmsg device structure
113 * data: Pointer to the Rx data
114 * len: Size of the Rx data
115 * priv: Private pointer to the channel
116 * addr: Address variable
117 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530118 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800119static int wdsp_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
120 int len, void *priv, u32 addr__unused)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530121{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800122 struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530123 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800124 unsigned long flags;
125 u8 *rx_buf;
126 u8 rsp_cnt = 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530127
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800128 if (!ch || !data) {
129 pr_err("%s: Invalid ch or data\n", __func__);
130 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530131 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800132
133 wpriv = ch->wpriv;
134 rx_buf = (u8 *)data;
135 if (len > WDSP_MAX_READ_SIZE) {
136 dev_info_ratelimited(wpriv->dev, "%s: Size %d is greater than allowed %d\n",
137 __func__, len, WDSP_MAX_READ_SIZE);
138 len = WDSP_MAX_READ_SIZE;
139 }
140 dev_dbg_ratelimited(wpriv->dev, "%s: copy into buffer %d\n", __func__,
141 wpriv->rsp_cnt);
142
143 if (wpriv->rsp_cnt >= RESP_QUEUE_SIZE) {
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800144 dev_info_ratelimited(wpriv->dev, "%s: Resp Queue is Full. Ignore new one.\n",
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800145 __func__);
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800146 return -EINVAL;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800147 }
148 spin_lock_irqsave(&wpriv->rsp_lock, flags);
149 rsp_cnt = wpriv->rsp_cnt;
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800150 memcpy(wpriv->rsp[wpriv->write_idx].buf, rx_buf, len);
151 wpriv->rsp[wpriv->write_idx].buf_size = len;
152 wpriv->write_idx = (wpriv->write_idx + 1) % RESP_QUEUE_SIZE;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800153 wpriv->rsp_cnt = ++rsp_cnt;
154 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
155
156 complete(&wpriv->rsp_complete);
157
158 return 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530159}
160
161/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800162 * wdsp_rpmsg_probe - Rpmsg channel probe function
163 * rpdev: Rpmsg device structure
164 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530165 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800166static int wdsp_rpmsg_probe(struct rpmsg_device *rpdev)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530167{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800168 struct wdsp_ch *ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530169
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800170 ch = wdsp_get_ch(rpdev->id.name);
171 if (!ch) {
172 dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n",
173 __func__, rpdev->id.name);
174 return -EINVAL;
175 }
176
177 dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Up]\n",
178 __func__, rpdev->id.name);
179
180 spin_lock(&ch->ch_lock);
181 ch->handle = rpdev;
182 ch->ch_state = WDSP_CH_CONNECTED;
183 spin_unlock(&ch->ch_lock);
184 dev_set_drvdata(&rpdev->dev, ch);
185 wake_up(&wpriv->ch_state_wait);
186
187 return 0;
188}
189
190/*
191 * wdsp_rpmsg_remove - Rpmsg channel remove function
192 * rpdev: Rpmsg device structure
193 */
194static void wdsp_rpmsg_remove(struct rpmsg_device *rpdev)
195{
196 struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
197
198 if (!ch) {
199 dev_err(&rpdev->dev, "%s: Invalid ch\n", __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530200 return;
201 }
202
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800203 dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Down]\n",
204 __func__, rpdev->id.name);
205 spin_lock(&ch->ch_lock);
206 ch->handle = NULL;
207 ch->ch_state = WDSP_CH_DISCONNECTED;
208 spin_unlock(&ch->ch_lock);
209 dev_set_drvdata(&rpdev->dev, NULL);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530210}
211
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800212static bool wdsp_is_ch_connected(struct wdsp_glink_priv *wpriv)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530213{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800214 int i;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530215
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800216 for (i = 0; i < wpriv->no_of_channels; i++) {
217 spin_lock(&wpriv->ch[i]->ch_lock);
218 if (wpriv->ch[i]->ch_state != WDSP_CH_CONNECTED) {
219 spin_unlock(&wpriv->ch[i]->ch_lock);
220 return false;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530221 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800222 spin_unlock(&wpriv->ch[i]->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530223 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800224 return true;
225}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530226
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800227static int wdsp_wait_for_all_ch_connect(struct wdsp_glink_priv *wpriv)
228{
229 int ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530230
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800231 ret = wait_event_timeout(wpriv->ch_state_wait,
232 wdsp_is_ch_connected(wpriv),
233 msecs_to_jiffies(TIMEOUT_MS));
234 if (!ret) {
235 dev_err_ratelimited(wpriv->dev, "%s: All channels are not connected\n",
236 __func__);
237 ret = -ETIMEDOUT;
238 goto err;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530239 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800240 ret = 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530241
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800242err:
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530243 return ret;
244}
245
246/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800247 * wdsp_tx_buf_work - Work queue function to send tx buffer to glink
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530248 * work: Work structure
249 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800250static void wdsp_tx_buf_work(struct work_struct *work)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530251{
252 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800253 struct wdsp_ch *ch;
254 struct wdsp_tx_buf *tx_buf;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530255 struct wdsp_write_pkt *wpkt;
256 struct wdsp_cmd_pkt *cpkt;
257 int ret = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800258 struct rpmsg_device *rpdev = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530259
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800260 tx_buf = container_of(work, struct wdsp_tx_buf,
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530261 tx_work);
262 ch = tx_buf->ch;
263 wpriv = ch->wpriv;
264 wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
265 cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800266
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530267 dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n",
268 __func__, cpkt->ch_name, cpkt->payload_size);
269
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800270 spin_lock(&ch->ch_lock);
271 rpdev = ch->handle;
Xiaoyu Ye7c54a182018-03-22 11:55:06 -0700272 if (rpdev && ch->ch_state == WDSP_CH_CONNECTED) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800273 spin_unlock(&ch->ch_lock);
274 ret = rpmsg_send(rpdev->ept, cpkt->payload,
275 cpkt->payload_size);
276 if (ret < 0)
277 dev_err(wpriv->dev, "%s: rpmsg send failed, ret = %d\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530278 __func__, ret);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530279 } else {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800280 spin_unlock(&ch->ch_lock);
Xiaoyu Ye7c54a182018-03-22 11:55:06 -0700281 if (rpdev)
282 dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
283 __func__, ch->ch_name);
284 else
285 dev_err(wpriv->dev, "%s: rpdev is NULL\n", __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530286 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800287 vfree(tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530288}
289
290/*
291 * wdsp_glink_read - Read API to send the data to userspace
292 * file: Pointer to the file structure
293 * buf: Pointer to the userspace buffer
294 * count: Number bytes to read from the file
295 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800296 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530297 */
298static ssize_t wdsp_glink_read(struct file *file, char __user *buf,
299 size_t count, loff_t *ppos)
300{
301 int ret = 0, ret1 = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800302 struct wdsp_rsp_que *read_rsp = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530303 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800304 unsigned long flags;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530305
306 wpriv = (struct wdsp_glink_priv *)file->private_data;
307 if (!wpriv) {
308 pr_err("%s: Invalid private data\n", __func__);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800309 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530310 }
311
312 if (count > WDSP_MAX_READ_SIZE) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700313 dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530314 __func__, count);
315 count = WDSP_MAX_READ_SIZE;
316 }
317 /*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800318 * Complete signal has given from gwdsp_rpmsg_callback()
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530319 * or from flush API. Also use interruptible wait_for_completion API
320 * to allow the system to go in suspend.
321 */
322 ret = wait_for_completion_interruptible(&wpriv->rsp_complete);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800323 if (ret < 0)
324 return ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530325
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800326 read_rsp = kzalloc(sizeof(struct wdsp_rsp_que), GFP_KERNEL);
327 if (!read_rsp)
328 return -ENOMEM;
329
330 spin_lock_irqsave(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530331 if (wpriv->rsp_cnt) {
332 wpriv->rsp_cnt--;
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800333 dev_dbg(wpriv->dev, "%s: rsp_cnt=%d read from buffer %d\n",
334 __func__, wpriv->rsp_cnt, wpriv->read_idx);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530335
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800336 memcpy(read_rsp, &wpriv->rsp[wpriv->read_idx],
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800337 sizeof(struct wdsp_rsp_que));
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800338 wpriv->read_idx = (wpriv->read_idx + 1) % RESP_QUEUE_SIZE;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800339 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
340
341 if (count < read_rsp->buf_size) {
342 ret1 = copy_to_user(buf, read_rsp->buf, count);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530343 /* Return the number of bytes copied */
344 ret = count;
345 } else {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800346 ret1 = copy_to_user(buf, read_rsp->buf,
347 read_rsp->buf_size);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530348 /* Return the number of bytes copied */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800349 ret = read_rsp->buf_size;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530350 }
351
352 if (ret1) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700353 dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n",
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800354 __func__, ret);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530355 ret = -EFAULT;
356 goto done;
357 }
358 } else {
359 /*
360 * This will execute only if flush API is called or
361 * something wrong with ref_cnt
362 */
363 dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__,
364 wpriv->rsp_cnt);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800365 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530366 ret = -EINVAL;
367 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530368
369done:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800370 kfree(read_rsp);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530371 return ret;
372}
373
374/*
375 * wdsp_glink_write - Write API to receive the data from userspace
376 * file: Pointer to the file structure
377 * buf: Pointer to the userspace buffer
378 * count: Number bytes to read from the file
379 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800380 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530381 */
382static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
383 size_t count, loff_t *ppos)
384{
385 int ret = 0, i, tx_buf_size;
386 struct wdsp_write_pkt *wpkt;
387 struct wdsp_cmd_pkt *cpkt;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800388 struct wdsp_tx_buf *tx_buf;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530389 struct wdsp_glink_priv *wpriv;
390 size_t pkt_max_size;
391
392 wpriv = (struct wdsp_glink_priv *)file->private_data;
393 if (!wpriv) {
394 pr_err("%s: Invalid private data\n", __func__);
395 ret = -EINVAL;
396 goto done;
397 }
398
399 if ((count < WDSP_WRITE_PKT_SIZE) ||
400 (count > WDSP_MAX_WRITE_SIZE)) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700401 dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530402 __func__, count);
403 ret = -EINVAL;
404 goto done;
405 }
406
407 dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count);
408
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800409 tx_buf_size = count + sizeof(struct wdsp_tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530410 tx_buf = vzalloc(tx_buf_size);
411 if (!tx_buf) {
412 ret = -ENOMEM;
413 goto done;
414 }
415
416 ret = copy_from_user(tx_buf->buf, buf, count);
417 if (ret) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700418 dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530419 __func__, ret);
420 ret = -EFAULT;
421 goto free_buf;
422 }
423
424 wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
425 switch (wpkt->pkt_type) {
426 case WDSP_REG_PKT:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800427 /* Keep this case to support backward compatibility */
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530428 vfree(tx_buf);
429 break;
430 case WDSP_READY_PKT:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800431 ret = wdsp_wait_for_all_ch_connect(wpriv);
432 if (ret < 0)
433 dev_err_ratelimited(wpriv->dev, "%s: Channels not in connected state\n",
434 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530435 vfree(tx_buf);
436 break;
437 case WDSP_CMD_PKT:
438 if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700439 dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530440 __func__, count);
441 ret = -EINVAL;
442 goto free_buf;
443 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530444 cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
445 pkt_max_size = sizeof(struct wdsp_write_pkt) +
446 sizeof(struct wdsp_cmd_pkt) +
447 cpkt->payload_size;
448 if (count < pkt_max_size) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700449 dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530450 __func__, count, pkt_max_size);
451 ret = -EINVAL;
452 goto free_buf;
453 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530454 for (i = 0; i < wpriv->no_of_channels; i++) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800455 if (!strcmp(cpkt->ch_name, wpriv->ch[i]->ch_name)) {
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530456 tx_buf->ch = wpriv->ch[i];
457 break;
458 }
459 }
460 if (!tx_buf->ch) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800461 dev_err_ratelimited(wpriv->dev, "%s: Failed to get channel\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530462 __func__);
463 ret = -EINVAL;
464 goto free_buf;
465 }
Vidyakumar Athota87c83b72017-09-19 12:06:46 -0700466 dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n",
467 __func__, cpkt->ch_name, pkt_max_size);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530468
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800469 spin_lock(&tx_buf->ch->ch_lock);
470 if (tx_buf->ch->ch_state != WDSP_CH_CONNECTED) {
471 spin_unlock(&tx_buf->ch->ch_lock);
472 ret = -ENETRESET;
473 dev_err_ratelimited(wpriv->dev, "%s: Channels are not in connected state\n",
474 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530475 goto free_buf;
476 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800477 spin_unlock(&tx_buf->ch->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530478
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800479 INIT_WORK(&tx_buf->tx_work, wdsp_tx_buf_work);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530480 queue_work(wpriv->work_queue, &tx_buf->tx_work);
481 break;
482 default:
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700483 dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n",
484 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530485 ret = -EINVAL;
486 vfree(tx_buf);
487 break;
488 }
489 goto done;
490
491free_buf:
492 vfree(tx_buf);
493
494done:
495 return ret;
496}
497
498/*
499 * wdsp_glink_open - Open API to initialize private data
500 * inode: Pointer to the inode structure
501 * file: Pointer to the file structure
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800502 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530503 */
504static int wdsp_glink_open(struct inode *inode, struct file *file)
505{
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530506
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800507 pr_debug("%s: wpriv = %pK\n", __func__, wpriv);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530508 file->private_data = wpriv;
509
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800510 return 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530511}
512
513/*
514 * wdsp_glink_flush - Flush API to unblock read.
515 * file: Pointer to the file structure
516 * id: Lock owner ID
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800517 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530518 */
519static int wdsp_glink_flush(struct file *file, fl_owner_t id)
520{
521 struct wdsp_glink_priv *wpriv;
522
523 wpriv = (struct wdsp_glink_priv *)file->private_data;
524 if (!wpriv) {
525 pr_err("%s: Invalid private data\n", __func__);
526 return -EINVAL;
527 }
528
529 complete(&wpriv->rsp_complete);
530
531 return 0;
532}
533
534/*
535 * wdsp_glink_release - Release API to clean up resources.
536 * Whenever a file structure is shared across multiple threads,
537 * release won't be invoked until all copies are closed
538 * (file->f_count.counter should be 0). If we need to flush pending
539 * data when any copy is closed, you should implement the flush method.
540 *
541 * inode: Pointer to the inode structure
542 * file: Pointer to the file structure
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800543 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530544 */
545static int wdsp_glink_release(struct inode *inode, struct file *file)
546{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800547 pr_debug("%s: file->private_data = %pK\n", __func__,
548 file->private_data);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530549 file->private_data = NULL;
550
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800551 return 0;
552}
553
554static struct rpmsg_driver wdsp_rpmsg_driver = {
555 .probe = wdsp_rpmsg_probe,
556 .remove = wdsp_rpmsg_remove,
557 .callback = wdsp_rpmsg_callback,
558 /* Update this dynamically before register_rpmsg() */
559 .id_table = NULL,
560 .drv = {
561 .name = "wdsp_rpmsg",
562 },
563};
564
565static int wdsp_register_rpmsg(struct platform_device *pdev,
566 struct wdsp_glink_dev *wdev)
567{
568 int ret = 0;
569 int i, no_of_channels;
570 struct rpmsg_device_id *wdsp_rpmsg_id_table, *id_table;
571 const char *ch_name = NULL;
572
573 wpriv = devm_kzalloc(&pdev->dev,
574 sizeof(struct wdsp_glink_priv), GFP_KERNEL);
575 if (!wpriv)
576 return -ENOMEM;
577
578 no_of_channels = of_property_count_strings(pdev->dev.of_node,
Vidyakumar Athotae75ea782018-03-14 15:00:12 -0700579 "qcom,wdsp-channels");
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800580 if (no_of_channels < 0) {
581 dev_err(&pdev->dev, "%s: channel name parse error %d\n",
582 __func__, no_of_channels);
583 return -EINVAL;
584 }
585
586 wpriv->ch = devm_kzalloc(&pdev->dev,
587 (sizeof(struct wdsp_glink_priv *) * no_of_channels),
588 GFP_KERNEL);
589 if (!wpriv->ch)
590 return -ENOMEM;
591
592 for (i = 0; i < no_of_channels; i++) {
593 ret = of_property_read_string_index(pdev->dev.of_node,
594 "qcom,wdsp-channels", i,
595 &ch_name);
596 if (ret) {
597 dev_err(&pdev->dev, "%s: channel name parse error %d\n",
598 __func__, ret);
599 return -EINVAL;
600 }
601 wpriv->ch[i] = devm_kzalloc(&pdev->dev,
602 sizeof(struct wdsp_glink_priv),
603 GFP_KERNEL);
604 if (!wpriv->ch[i])
605 return -ENOMEM;
606
607 strlcpy(wpriv->ch[i]->ch_name, ch_name, RPMSG_NAME_SIZE);
608 wpriv->ch[i]->wpriv = wpriv;
609 spin_lock_init(&wpriv->ch[i]->ch_lock);
610 }
611 init_waitqueue_head(&wpriv->ch_state_wait);
612 init_completion(&wpriv->rsp_complete);
613 spin_lock_init(&wpriv->rsp_lock);
614
615 wpriv->wdev = wdev;
616 wpriv->dev = wdev->dev;
617 wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq");
618 if (!wpriv->work_queue) {
619 dev_err(&pdev->dev, "%s: Error creating wdsp_glink_wq\n",
620 __func__);
621 return -EINVAL;
622 }
623
624 wdsp_rpmsg_id_table = devm_kzalloc(&pdev->dev,
625 (sizeof(struct rpmsg_device_id) *
626 (no_of_channels + 1)),
627 GFP_KERNEL);
628 if (!wdsp_rpmsg_id_table) {
629 ret = -ENOMEM;
630 goto err;
631 }
632
633 wpriv->no_of_channels = no_of_channels;
634 id_table = wdsp_rpmsg_id_table;
635 for (i = 0; i < no_of_channels; i++) {
636 strlcpy(id_table->name, wpriv->ch[i]->ch_name,
637 RPMSG_NAME_SIZE);
638 id_table++;
639 }
640 wdsp_rpmsg_driver.id_table = wdsp_rpmsg_id_table;
641 ret = register_rpmsg_driver(&wdsp_rpmsg_driver);
642 if (ret < 0) {
643 dev_err(&pdev->dev, "%s: Rpmsg driver register failed, err = %d\n",
644 __func__, ret);
645 goto err;
646 }
647
648 return 0;
649
650err:
651 destroy_workqueue(wpriv->work_queue);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530652 return ret;
653}
654
655static const struct file_operations wdsp_glink_fops = {
656 .owner = THIS_MODULE,
657 .open = wdsp_glink_open,
658 .read = wdsp_glink_read,
659 .write = wdsp_glink_write,
660 .flush = wdsp_glink_flush,
661 .release = wdsp_glink_release,
662};
663
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530664static int wdsp_glink_probe(struct platform_device *pdev)
665{
666 int ret;
667 struct wdsp_glink_dev *wdev;
668
669 wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
670 if (!wdev) {
671 ret = -ENOMEM;
672 goto done;
673 }
674
675 ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT,
676 WDSP_GLINK_DRIVER_NAME);
677 if (ret < 0) {
678 dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n",
679 __func__, ret);
680 goto err_chrdev;
681 }
682
683 wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME);
684 if (IS_ERR(wdev->cls)) {
685 ret = PTR_ERR(wdev->cls);
686 dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n",
687 __func__, ret);
688 goto err_class;
689 }
690
691 wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num,
692 NULL, WDSP_GLINK_DRIVER_NAME);
693 if (IS_ERR(wdev->dev)) {
694 ret = PTR_ERR(wdev->dev);
695 dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n",
696 __func__, ret);
697 goto err_dev_create;
698 }
699
700 cdev_init(&wdev->cdev, &wdsp_glink_fops);
701 ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT);
702 if (ret < 0) {
703 dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n",
704 __func__, ret);
705 goto err_cdev_add;
706 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800707
708 ret = wdsp_register_rpmsg(pdev, wdev);
709 if (ret < 0) {
710 dev_err(&pdev->dev, "%s: Failed to register with rpmsg, err = %d\n",
711 __func__, ret);
712 goto err_cdev_add;
713 }
714 platform_set_drvdata(pdev, wpriv);
715
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530716 goto done;
717
718err_cdev_add:
719 device_destroy(wdev->cls, wdev->dev_num);
720
721err_dev_create:
722 class_destroy(wdev->cls);
723
724err_class:
725 unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
726
727err_chrdev:
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530728done:
729 return ret;
730}
731
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530732static int wdsp_glink_remove(struct platform_device *pdev)
733{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800734 struct wdsp_glink_priv *wpriv = platform_get_drvdata(pdev);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530735
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800736 unregister_rpmsg_driver(&wdsp_rpmsg_driver);
737
738 if (wpriv) {
739 flush_workqueue(wpriv->work_queue);
740 destroy_workqueue(wpriv->work_queue);
741 if (wpriv->wdev) {
742 cdev_del(&wpriv->wdev->cdev);
743 device_destroy(wpriv->wdev->cls, wpriv->wdev->dev_num);
744 class_destroy(wpriv->wdev->cls);
745 unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
746 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530747 }
748
749 return 0;
750}
751
752static const struct of_device_id wdsp_glink_of_match[] = {
753 {.compatible = "qcom,wcd-dsp-glink"},
754 { }
755};
756MODULE_DEVICE_TABLE(of, wdsp_glink_of_match);
757
758static struct platform_driver wdsp_glink_driver = {
759 .probe = wdsp_glink_probe,
760 .remove = wdsp_glink_remove,
761 .driver = {
762 .name = WDSP_GLINK_DRIVER_NAME,
763 .owner = THIS_MODULE,
764 .of_match_table = wdsp_glink_of_match,
765 },
766};
767
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530768static int __init wdsp_glink_init(void)
769{
770 return platform_driver_register(&wdsp_glink_driver);
771}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530772
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530773static void __exit wdsp_glink_exit(void)
774{
775 platform_driver_unregister(&wdsp_glink_driver);
776}
777
778module_init(wdsp_glink_init);
779module_exit(wdsp_glink_exit);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530780MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver");
781MODULE_LICENSE("GPL v2");