blob: 04df026579ad7838adf2fe9a05417f9a84ce8e04 [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
Laxminath Kasamd1f36192017-08-03 18:54:01 +053036#define TIMEOUT_MS 1000
37
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];
Laxminath Kasamd1f36192017-08-03 18:54:01 +053082 struct completion rsp_complete;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080083 spinlock_t rsp_lock;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053084
85 /* Glink channel related */
Vidyakumar Athota7206c722017-12-05 11:43:47 -080086 int no_of_channels;
87 struct wdsp_ch **ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053088 struct workqueue_struct *work_queue;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080089 /* Wait for all channels state before sending any command */
90 wait_queue_head_t ch_state_wait;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053091
Vidyakumar Athota7206c722017-12-05 11:43:47 -080092 struct wdsp_glink_dev *wdev;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053093 struct device *dev;
94};
Vidyakumar Athota7206c722017-12-05 11:43:47 -080095static struct wdsp_glink_priv *wpriv;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053096
Vidyakumar Athota7206c722017-12-05 11:43:47 -080097static struct wdsp_ch *wdsp_get_ch(char *ch_name)
Laxminath Kasamd1f36192017-08-03 18:54:01 +053098{
99 int i;
100
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530101 for (i = 0; i < wpriv->no_of_channels; i++) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800102 if (!strcmp(ch_name, wpriv->ch[i]->ch_name))
103 return wpriv->ch[i];
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530104 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800105 return NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530106}
107
108/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800109 * wdsp_rpmsg_callback - Rpmsg callback for responses
110 * rpdev: Rpmsg device structure
111 * data: Pointer to the Rx data
112 * len: Size of the Rx data
113 * priv: Private pointer to the channel
114 * addr: Address variable
115 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530116 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800117static int wdsp_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
118 int len, void *priv, u32 addr__unused)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530119{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800120 struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530121 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800122 unsigned long flags;
123 u8 *rx_buf;
124 u8 rsp_cnt = 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530125
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800126 if (!ch || !data) {
127 pr_err("%s: Invalid ch or data\n", __func__);
128 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530129 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800130
131 wpriv = ch->wpriv;
132 rx_buf = (u8 *)data;
133 if (len > WDSP_MAX_READ_SIZE) {
134 dev_info_ratelimited(wpriv->dev, "%s: Size %d is greater than allowed %d\n",
135 __func__, len, WDSP_MAX_READ_SIZE);
136 len = WDSP_MAX_READ_SIZE;
137 }
138 dev_dbg_ratelimited(wpriv->dev, "%s: copy into buffer %d\n", __func__,
139 wpriv->rsp_cnt);
140
141 if (wpriv->rsp_cnt >= RESP_QUEUE_SIZE) {
142 dev_info_ratelimited(wpriv->dev, "%s: Resp Queue is Full\n",
143 __func__);
144 rsp_cnt = 0;
145 }
146 spin_lock_irqsave(&wpriv->rsp_lock, flags);
147 rsp_cnt = wpriv->rsp_cnt;
148 memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, len);
149 wpriv->rsp[rsp_cnt].buf_size = len;
150 wpriv->rsp_cnt = ++rsp_cnt;
151 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
152
153 complete(&wpriv->rsp_complete);
154
155 return 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530156}
157
158/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800159 * wdsp_rpmsg_probe - Rpmsg channel probe function
160 * rpdev: Rpmsg device structure
161 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530162 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800163static int wdsp_rpmsg_probe(struct rpmsg_device *rpdev)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530164{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800165 struct wdsp_ch *ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530166
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800167 ch = wdsp_get_ch(rpdev->id.name);
168 if (!ch) {
169 dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n",
170 __func__, rpdev->id.name);
171 return -EINVAL;
172 }
173
174 dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Up]\n",
175 __func__, rpdev->id.name);
176
177 spin_lock(&ch->ch_lock);
178 ch->handle = rpdev;
179 ch->ch_state = WDSP_CH_CONNECTED;
180 spin_unlock(&ch->ch_lock);
181 dev_set_drvdata(&rpdev->dev, ch);
182 wake_up(&wpriv->ch_state_wait);
183
184 return 0;
185}
186
187/*
188 * wdsp_rpmsg_remove - Rpmsg channel remove function
189 * rpdev: Rpmsg device structure
190 */
191static void wdsp_rpmsg_remove(struct rpmsg_device *rpdev)
192{
193 struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
194
195 if (!ch) {
196 dev_err(&rpdev->dev, "%s: Invalid ch\n", __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530197 return;
198 }
199
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800200 dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Down]\n",
201 __func__, rpdev->id.name);
202 spin_lock(&ch->ch_lock);
203 ch->handle = NULL;
204 ch->ch_state = WDSP_CH_DISCONNECTED;
205 spin_unlock(&ch->ch_lock);
206 dev_set_drvdata(&rpdev->dev, NULL);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530207}
208
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800209static bool wdsp_is_ch_connected(struct wdsp_glink_priv *wpriv)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530210{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800211 int i;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530212
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800213 for (i = 0; i < wpriv->no_of_channels; i++) {
214 spin_lock(&wpriv->ch[i]->ch_lock);
215 if (wpriv->ch[i]->ch_state != WDSP_CH_CONNECTED) {
216 spin_unlock(&wpriv->ch[i]->ch_lock);
217 return false;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530218 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800219 spin_unlock(&wpriv->ch[i]->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530220 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800221 return true;
222}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530223
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800224static int wdsp_wait_for_all_ch_connect(struct wdsp_glink_priv *wpriv)
225{
226 int ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530227
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800228 ret = wait_event_timeout(wpriv->ch_state_wait,
229 wdsp_is_ch_connected(wpriv),
230 msecs_to_jiffies(TIMEOUT_MS));
231 if (!ret) {
232 dev_err_ratelimited(wpriv->dev, "%s: All channels are not connected\n",
233 __func__);
234 ret = -ETIMEDOUT;
235 goto err;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530236 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800237 ret = 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530238
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800239err:
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530240 return ret;
241}
242
243/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800244 * wdsp_tx_buf_work - Work queue function to send tx buffer to glink
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530245 * work: Work structure
246 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800247static void wdsp_tx_buf_work(struct work_struct *work)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530248{
249 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800250 struct wdsp_ch *ch;
251 struct wdsp_tx_buf *tx_buf;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530252 struct wdsp_write_pkt *wpkt;
253 struct wdsp_cmd_pkt *cpkt;
254 int ret = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800255 struct rpmsg_device *rpdev = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530256
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800257 tx_buf = container_of(work, struct wdsp_tx_buf,
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530258 tx_work);
259 ch = tx_buf->ch;
260 wpriv = ch->wpriv;
261 wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
262 cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800263
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530264 dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n",
265 __func__, cpkt->ch_name, cpkt->payload_size);
266
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800267 spin_lock(&ch->ch_lock);
268 rpdev = ch->handle;
269 if (rpdev || ch->ch_state == WDSP_CH_CONNECTED) {
270 spin_unlock(&ch->ch_lock);
271 ret = rpmsg_send(rpdev->ept, cpkt->payload,
272 cpkt->payload_size);
273 if (ret < 0)
274 dev_err(wpriv->dev, "%s: rpmsg send failed, ret = %d\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530275 __func__, ret);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530276 } else {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800277 spin_unlock(&ch->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530278 dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800279 __func__, ch->ch_name);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530280 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800281 vfree(tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530282}
283
284/*
285 * wdsp_glink_read - Read API to send the data to userspace
286 * file: Pointer to the file structure
287 * buf: Pointer to the userspace buffer
288 * count: Number bytes to read from the file
289 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800290 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530291 */
292static ssize_t wdsp_glink_read(struct file *file, char __user *buf,
293 size_t count, loff_t *ppos)
294{
295 int ret = 0, ret1 = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800296 struct wdsp_rsp_que *read_rsp = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530297 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800298 unsigned long flags;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530299
300 wpriv = (struct wdsp_glink_priv *)file->private_data;
301 if (!wpriv) {
302 pr_err("%s: Invalid private data\n", __func__);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800303 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530304 }
305
306 if (count > WDSP_MAX_READ_SIZE) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700307 dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530308 __func__, count);
309 count = WDSP_MAX_READ_SIZE;
310 }
311 /*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800312 * Complete signal has given from gwdsp_rpmsg_callback()
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530313 * or from flush API. Also use interruptible wait_for_completion API
314 * to allow the system to go in suspend.
315 */
316 ret = wait_for_completion_interruptible(&wpriv->rsp_complete);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800317 if (ret < 0)
318 return ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530319
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800320 read_rsp = kzalloc(sizeof(struct wdsp_rsp_que), GFP_KERNEL);
321 if (!read_rsp)
322 return -ENOMEM;
323
324 spin_lock_irqsave(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530325 if (wpriv->rsp_cnt) {
326 wpriv->rsp_cnt--;
327 dev_dbg(wpriv->dev, "%s: read from buffer %d\n",
328 __func__, wpriv->rsp_cnt);
329
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800330 memcpy(read_rsp, &wpriv->rsp[wpriv->rsp_cnt],
331 sizeof(struct wdsp_rsp_que));
332 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
333
334 if (count < read_rsp->buf_size) {
335 ret1 = copy_to_user(buf, read_rsp->buf, count);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530336 /* Return the number of bytes copied */
337 ret = count;
338 } else {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800339 ret1 = copy_to_user(buf, read_rsp->buf,
340 read_rsp->buf_size);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530341 /* Return the number of bytes copied */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800342 ret = read_rsp->buf_size;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530343 }
344
345 if (ret1) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700346 dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n",
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800347 __func__, ret);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530348 ret = -EFAULT;
349 goto done;
350 }
351 } else {
352 /*
353 * This will execute only if flush API is called or
354 * something wrong with ref_cnt
355 */
356 dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__,
357 wpriv->rsp_cnt);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800358 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530359 ret = -EINVAL;
360 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530361
362done:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800363 kfree(read_rsp);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530364 return ret;
365}
366
367/*
368 * wdsp_glink_write - Write API to receive the data from userspace
369 * file: Pointer to the file structure
370 * buf: Pointer to the userspace buffer
371 * count: Number bytes to read from the file
372 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800373 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530374 */
375static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
376 size_t count, loff_t *ppos)
377{
378 int ret = 0, i, tx_buf_size;
379 struct wdsp_write_pkt *wpkt;
380 struct wdsp_cmd_pkt *cpkt;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800381 struct wdsp_tx_buf *tx_buf;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530382 struct wdsp_glink_priv *wpriv;
383 size_t pkt_max_size;
384
385 wpriv = (struct wdsp_glink_priv *)file->private_data;
386 if (!wpriv) {
387 pr_err("%s: Invalid private data\n", __func__);
388 ret = -EINVAL;
389 goto done;
390 }
391
392 if ((count < WDSP_WRITE_PKT_SIZE) ||
393 (count > WDSP_MAX_WRITE_SIZE)) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700394 dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530395 __func__, count);
396 ret = -EINVAL;
397 goto done;
398 }
399
400 dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count);
401
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800402 tx_buf_size = count + sizeof(struct wdsp_tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530403 tx_buf = vzalloc(tx_buf_size);
404 if (!tx_buf) {
405 ret = -ENOMEM;
406 goto done;
407 }
408
409 ret = copy_from_user(tx_buf->buf, buf, count);
410 if (ret) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700411 dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530412 __func__, ret);
413 ret = -EFAULT;
414 goto free_buf;
415 }
416
417 wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
418 switch (wpkt->pkt_type) {
419 case WDSP_REG_PKT:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800420 /* Keep this case to support backward compatibility */
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530421 vfree(tx_buf);
422 break;
423 case WDSP_READY_PKT:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800424 ret = wdsp_wait_for_all_ch_connect(wpriv);
425 if (ret < 0)
426 dev_err_ratelimited(wpriv->dev, "%s: Channels not in connected state\n",
427 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530428 vfree(tx_buf);
429 break;
430 case WDSP_CMD_PKT:
431 if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700432 dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530433 __func__, count);
434 ret = -EINVAL;
435 goto free_buf;
436 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530437 cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
438 pkt_max_size = sizeof(struct wdsp_write_pkt) +
439 sizeof(struct wdsp_cmd_pkt) +
440 cpkt->payload_size;
441 if (count < pkt_max_size) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700442 dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530443 __func__, count, pkt_max_size);
444 ret = -EINVAL;
445 goto free_buf;
446 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530447 for (i = 0; i < wpriv->no_of_channels; i++) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800448 if (!strcmp(cpkt->ch_name, wpriv->ch[i]->ch_name)) {
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530449 tx_buf->ch = wpriv->ch[i];
450 break;
451 }
452 }
453 if (!tx_buf->ch) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800454 dev_err_ratelimited(wpriv->dev, "%s: Failed to get channel\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530455 __func__);
456 ret = -EINVAL;
457 goto free_buf;
458 }
Vidyakumar Athota87c83b72017-09-19 12:06:46 -0700459 dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n",
460 __func__, cpkt->ch_name, pkt_max_size);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530461
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800462 spin_lock(&tx_buf->ch->ch_lock);
463 if (tx_buf->ch->ch_state != WDSP_CH_CONNECTED) {
464 spin_unlock(&tx_buf->ch->ch_lock);
465 ret = -ENETRESET;
466 dev_err_ratelimited(wpriv->dev, "%s: Channels are not in connected state\n",
467 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530468 goto free_buf;
469 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800470 spin_unlock(&tx_buf->ch->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530471
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800472 INIT_WORK(&tx_buf->tx_work, wdsp_tx_buf_work);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530473 queue_work(wpriv->work_queue, &tx_buf->tx_work);
474 break;
475 default:
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700476 dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n",
477 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530478 ret = -EINVAL;
479 vfree(tx_buf);
480 break;
481 }
482 goto done;
483
484free_buf:
485 vfree(tx_buf);
486
487done:
488 return ret;
489}
490
491/*
492 * wdsp_glink_open - Open API to initialize private data
493 * inode: Pointer to the inode structure
494 * file: Pointer to the file structure
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800495 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530496 */
497static int wdsp_glink_open(struct inode *inode, struct file *file)
498{
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530499
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800500 pr_debug("%s: wpriv = %pK\n", __func__, wpriv);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530501 file->private_data = wpriv;
502
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800503 return 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530504}
505
506/*
507 * wdsp_glink_flush - Flush API to unblock read.
508 * file: Pointer to the file structure
509 * id: Lock owner ID
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800510 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530511 */
512static int wdsp_glink_flush(struct file *file, fl_owner_t id)
513{
514 struct wdsp_glink_priv *wpriv;
515
516 wpriv = (struct wdsp_glink_priv *)file->private_data;
517 if (!wpriv) {
518 pr_err("%s: Invalid private data\n", __func__);
519 return -EINVAL;
520 }
521
522 complete(&wpriv->rsp_complete);
523
524 return 0;
525}
526
527/*
528 * wdsp_glink_release - Release API to clean up resources.
529 * Whenever a file structure is shared across multiple threads,
530 * release won't be invoked until all copies are closed
531 * (file->f_count.counter should be 0). If we need to flush pending
532 * data when any copy is closed, you should implement the flush method.
533 *
534 * inode: Pointer to the inode structure
535 * file: Pointer to the file structure
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800536 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530537 */
538static int wdsp_glink_release(struct inode *inode, struct file *file)
539{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800540 pr_debug("%s: file->private_data = %pK\n", __func__,
541 file->private_data);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530542 file->private_data = NULL;
543
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800544 return 0;
545}
546
547static struct rpmsg_driver wdsp_rpmsg_driver = {
548 .probe = wdsp_rpmsg_probe,
549 .remove = wdsp_rpmsg_remove,
550 .callback = wdsp_rpmsg_callback,
551 /* Update this dynamically before register_rpmsg() */
552 .id_table = NULL,
553 .drv = {
554 .name = "wdsp_rpmsg",
555 },
556};
557
558static int wdsp_register_rpmsg(struct platform_device *pdev,
559 struct wdsp_glink_dev *wdev)
560{
561 int ret = 0;
562 int i, no_of_channels;
563 struct rpmsg_device_id *wdsp_rpmsg_id_table, *id_table;
564 const char *ch_name = NULL;
565
566 wpriv = devm_kzalloc(&pdev->dev,
567 sizeof(struct wdsp_glink_priv), GFP_KERNEL);
568 if (!wpriv)
569 return -ENOMEM;
570
571 no_of_channels = of_property_count_strings(pdev->dev.of_node,
572 "qcom,wdsp_channles");
573 if (no_of_channels < 0) {
574 dev_err(&pdev->dev, "%s: channel name parse error %d\n",
575 __func__, no_of_channels);
576 return -EINVAL;
577 }
578
579 wpriv->ch = devm_kzalloc(&pdev->dev,
580 (sizeof(struct wdsp_glink_priv *) * no_of_channels),
581 GFP_KERNEL);
582 if (!wpriv->ch)
583 return -ENOMEM;
584
585 for (i = 0; i < no_of_channels; i++) {
586 ret = of_property_read_string_index(pdev->dev.of_node,
587 "qcom,wdsp-channels", i,
588 &ch_name);
589 if (ret) {
590 dev_err(&pdev->dev, "%s: channel name parse error %d\n",
591 __func__, ret);
592 return -EINVAL;
593 }
594 wpriv->ch[i] = devm_kzalloc(&pdev->dev,
595 sizeof(struct wdsp_glink_priv),
596 GFP_KERNEL);
597 if (!wpriv->ch[i])
598 return -ENOMEM;
599
600 strlcpy(wpriv->ch[i]->ch_name, ch_name, RPMSG_NAME_SIZE);
601 wpriv->ch[i]->wpriv = wpriv;
602 spin_lock_init(&wpriv->ch[i]->ch_lock);
603 }
604 init_waitqueue_head(&wpriv->ch_state_wait);
605 init_completion(&wpriv->rsp_complete);
606 spin_lock_init(&wpriv->rsp_lock);
607
608 wpriv->wdev = wdev;
609 wpriv->dev = wdev->dev;
610 wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq");
611 if (!wpriv->work_queue) {
612 dev_err(&pdev->dev, "%s: Error creating wdsp_glink_wq\n",
613 __func__);
614 return -EINVAL;
615 }
616
617 wdsp_rpmsg_id_table = devm_kzalloc(&pdev->dev,
618 (sizeof(struct rpmsg_device_id) *
619 (no_of_channels + 1)),
620 GFP_KERNEL);
621 if (!wdsp_rpmsg_id_table) {
622 ret = -ENOMEM;
623 goto err;
624 }
625
626 wpriv->no_of_channels = no_of_channels;
627 id_table = wdsp_rpmsg_id_table;
628 for (i = 0; i < no_of_channels; i++) {
629 strlcpy(id_table->name, wpriv->ch[i]->ch_name,
630 RPMSG_NAME_SIZE);
631 id_table++;
632 }
633 wdsp_rpmsg_driver.id_table = wdsp_rpmsg_id_table;
634 ret = register_rpmsg_driver(&wdsp_rpmsg_driver);
635 if (ret < 0) {
636 dev_err(&pdev->dev, "%s: Rpmsg driver register failed, err = %d\n",
637 __func__, ret);
638 goto err;
639 }
640
641 return 0;
642
643err:
644 destroy_workqueue(wpriv->work_queue);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530645 return ret;
646}
647
648static const struct file_operations wdsp_glink_fops = {
649 .owner = THIS_MODULE,
650 .open = wdsp_glink_open,
651 .read = wdsp_glink_read,
652 .write = wdsp_glink_write,
653 .flush = wdsp_glink_flush,
654 .release = wdsp_glink_release,
655};
656
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530657static int wdsp_glink_probe(struct platform_device *pdev)
658{
659 int ret;
660 struct wdsp_glink_dev *wdev;
661
662 wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
663 if (!wdev) {
664 ret = -ENOMEM;
665 goto done;
666 }
667
668 ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT,
669 WDSP_GLINK_DRIVER_NAME);
670 if (ret < 0) {
671 dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n",
672 __func__, ret);
673 goto err_chrdev;
674 }
675
676 wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME);
677 if (IS_ERR(wdev->cls)) {
678 ret = PTR_ERR(wdev->cls);
679 dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n",
680 __func__, ret);
681 goto err_class;
682 }
683
684 wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num,
685 NULL, WDSP_GLINK_DRIVER_NAME);
686 if (IS_ERR(wdev->dev)) {
687 ret = PTR_ERR(wdev->dev);
688 dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n",
689 __func__, ret);
690 goto err_dev_create;
691 }
692
693 cdev_init(&wdev->cdev, &wdsp_glink_fops);
694 ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT);
695 if (ret < 0) {
696 dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n",
697 __func__, ret);
698 goto err_cdev_add;
699 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800700
701 ret = wdsp_register_rpmsg(pdev, wdev);
702 if (ret < 0) {
703 dev_err(&pdev->dev, "%s: Failed to register with rpmsg, err = %d\n",
704 __func__, ret);
705 goto err_cdev_add;
706 }
707 platform_set_drvdata(pdev, wpriv);
708
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530709 goto done;
710
711err_cdev_add:
712 device_destroy(wdev->cls, wdev->dev_num);
713
714err_dev_create:
715 class_destroy(wdev->cls);
716
717err_class:
718 unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
719
720err_chrdev:
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530721done:
722 return ret;
723}
724
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530725static int wdsp_glink_remove(struct platform_device *pdev)
726{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800727 struct wdsp_glink_priv *wpriv = platform_get_drvdata(pdev);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530728
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800729 unregister_rpmsg_driver(&wdsp_rpmsg_driver);
730
731 if (wpriv) {
732 flush_workqueue(wpriv->work_queue);
733 destroy_workqueue(wpriv->work_queue);
734 if (wpriv->wdev) {
735 cdev_del(&wpriv->wdev->cdev);
736 device_destroy(wpriv->wdev->cls, wpriv->wdev->dev_num);
737 class_destroy(wpriv->wdev->cls);
738 unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
739 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530740 }
741
742 return 0;
743}
744
745static const struct of_device_id wdsp_glink_of_match[] = {
746 {.compatible = "qcom,wcd-dsp-glink"},
747 { }
748};
749MODULE_DEVICE_TABLE(of, wdsp_glink_of_match);
750
751static struct platform_driver wdsp_glink_driver = {
752 .probe = wdsp_glink_probe,
753 .remove = wdsp_glink_remove,
754 .driver = {
755 .name = WDSP_GLINK_DRIVER_NAME,
756 .owner = THIS_MODULE,
757 .of_match_table = wdsp_glink_of_match,
758 },
759};
760
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530761static int __init wdsp_glink_init(void)
762{
763 return platform_driver_register(&wdsp_glink_driver);
764}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530765
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530766static void __exit wdsp_glink_exit(void)
767{
768 platform_driver_unregister(&wdsp_glink_driver);
769}
770
771module_init(wdsp_glink_init);
772module_exit(wdsp_glink_exit);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530773MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver");
774MODULE_LICENSE("GPL v2");