blob: 21da65aae069b1e3051a4f74f0c7c181f97d9e54 [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;
Xiaoyu Ye7c54a182018-03-22 11:55:06 -0700269 if (rpdev && ch->ch_state == WDSP_CH_CONNECTED) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800270 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);
Xiaoyu Ye7c54a182018-03-22 11:55:06 -0700278 if (rpdev)
279 dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
280 __func__, ch->ch_name);
281 else
282 dev_err(wpriv->dev, "%s: rpdev is NULL\n", __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530283 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800284 vfree(tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530285}
286
287/*
288 * wdsp_glink_read - Read API to send the data to userspace
289 * file: Pointer to the file structure
290 * buf: Pointer to the userspace buffer
291 * count: Number bytes to read from the file
292 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800293 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530294 */
295static ssize_t wdsp_glink_read(struct file *file, char __user *buf,
296 size_t count, loff_t *ppos)
297{
298 int ret = 0, ret1 = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800299 struct wdsp_rsp_que *read_rsp = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530300 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800301 unsigned long flags;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530302
303 wpriv = (struct wdsp_glink_priv *)file->private_data;
304 if (!wpriv) {
305 pr_err("%s: Invalid private data\n", __func__);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800306 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530307 }
308
309 if (count > WDSP_MAX_READ_SIZE) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700310 dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530311 __func__, count);
312 count = WDSP_MAX_READ_SIZE;
313 }
314 /*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800315 * Complete signal has given from gwdsp_rpmsg_callback()
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530316 * or from flush API. Also use interruptible wait_for_completion API
317 * to allow the system to go in suspend.
318 */
319 ret = wait_for_completion_interruptible(&wpriv->rsp_complete);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800320 if (ret < 0)
321 return ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530322
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800323 read_rsp = kzalloc(sizeof(struct wdsp_rsp_que), GFP_KERNEL);
324 if (!read_rsp)
325 return -ENOMEM;
326
327 spin_lock_irqsave(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530328 if (wpriv->rsp_cnt) {
329 wpriv->rsp_cnt--;
330 dev_dbg(wpriv->dev, "%s: read from buffer %d\n",
331 __func__, wpriv->rsp_cnt);
332
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800333 memcpy(read_rsp, &wpriv->rsp[wpriv->rsp_cnt],
334 sizeof(struct wdsp_rsp_que));
335 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
336
337 if (count < read_rsp->buf_size) {
338 ret1 = copy_to_user(buf, read_rsp->buf, count);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530339 /* Return the number of bytes copied */
340 ret = count;
341 } else {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800342 ret1 = copy_to_user(buf, read_rsp->buf,
343 read_rsp->buf_size);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530344 /* Return the number of bytes copied */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800345 ret = read_rsp->buf_size;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530346 }
347
348 if (ret1) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700349 dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n",
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800350 __func__, ret);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530351 ret = -EFAULT;
352 goto done;
353 }
354 } else {
355 /*
356 * This will execute only if flush API is called or
357 * something wrong with ref_cnt
358 */
359 dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__,
360 wpriv->rsp_cnt);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800361 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530362 ret = -EINVAL;
363 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530364
365done:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800366 kfree(read_rsp);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530367 return ret;
368}
369
370/*
371 * wdsp_glink_write - Write API to receive the data from userspace
372 * file: Pointer to the file structure
373 * buf: Pointer to the userspace buffer
374 * count: Number bytes to read from the file
375 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800376 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530377 */
378static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
379 size_t count, loff_t *ppos)
380{
381 int ret = 0, i, tx_buf_size;
382 struct wdsp_write_pkt *wpkt;
383 struct wdsp_cmd_pkt *cpkt;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800384 struct wdsp_tx_buf *tx_buf;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530385 struct wdsp_glink_priv *wpriv;
386 size_t pkt_max_size;
387
388 wpriv = (struct wdsp_glink_priv *)file->private_data;
389 if (!wpriv) {
390 pr_err("%s: Invalid private data\n", __func__);
391 ret = -EINVAL;
392 goto done;
393 }
394
395 if ((count < WDSP_WRITE_PKT_SIZE) ||
396 (count > WDSP_MAX_WRITE_SIZE)) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700397 dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530398 __func__, count);
399 ret = -EINVAL;
400 goto done;
401 }
402
403 dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count);
404
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800405 tx_buf_size = count + sizeof(struct wdsp_tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530406 tx_buf = vzalloc(tx_buf_size);
407 if (!tx_buf) {
408 ret = -ENOMEM;
409 goto done;
410 }
411
412 ret = copy_from_user(tx_buf->buf, buf, count);
413 if (ret) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700414 dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530415 __func__, ret);
416 ret = -EFAULT;
417 goto free_buf;
418 }
419
420 wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
421 switch (wpkt->pkt_type) {
422 case WDSP_REG_PKT:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800423 /* Keep this case to support backward compatibility */
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530424 vfree(tx_buf);
425 break;
426 case WDSP_READY_PKT:
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800427 ret = wdsp_wait_for_all_ch_connect(wpriv);
428 if (ret < 0)
429 dev_err_ratelimited(wpriv->dev, "%s: Channels not in connected state\n",
430 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530431 vfree(tx_buf);
432 break;
433 case WDSP_CMD_PKT:
434 if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700435 dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530436 __func__, count);
437 ret = -EINVAL;
438 goto free_buf;
439 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530440 cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
441 pkt_max_size = sizeof(struct wdsp_write_pkt) +
442 sizeof(struct wdsp_cmd_pkt) +
443 cpkt->payload_size;
444 if (count < pkt_max_size) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700445 dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530446 __func__, count, pkt_max_size);
447 ret = -EINVAL;
448 goto free_buf;
449 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530450 for (i = 0; i < wpriv->no_of_channels; i++) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800451 if (!strcmp(cpkt->ch_name, wpriv->ch[i]->ch_name)) {
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530452 tx_buf->ch = wpriv->ch[i];
453 break;
454 }
455 }
456 if (!tx_buf->ch) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800457 dev_err_ratelimited(wpriv->dev, "%s: Failed to get channel\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530458 __func__);
459 ret = -EINVAL;
460 goto free_buf;
461 }
Vidyakumar Athota87c83b72017-09-19 12:06:46 -0700462 dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n",
463 __func__, cpkt->ch_name, pkt_max_size);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530464
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800465 spin_lock(&tx_buf->ch->ch_lock);
466 if (tx_buf->ch->ch_state != WDSP_CH_CONNECTED) {
467 spin_unlock(&tx_buf->ch->ch_lock);
468 ret = -ENETRESET;
469 dev_err_ratelimited(wpriv->dev, "%s: Channels are not in connected state\n",
470 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530471 goto free_buf;
472 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800473 spin_unlock(&tx_buf->ch->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530474
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800475 INIT_WORK(&tx_buf->tx_work, wdsp_tx_buf_work);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530476 queue_work(wpriv->work_queue, &tx_buf->tx_work);
477 break;
478 default:
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700479 dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n",
480 __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530481 ret = -EINVAL;
482 vfree(tx_buf);
483 break;
484 }
485 goto done;
486
487free_buf:
488 vfree(tx_buf);
489
490done:
491 return ret;
492}
493
494/*
495 * wdsp_glink_open - Open API to initialize private data
496 * inode: Pointer to the inode structure
497 * file: Pointer to the file structure
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800498 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530499 */
500static int wdsp_glink_open(struct inode *inode, struct file *file)
501{
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530502
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800503 pr_debug("%s: wpriv = %pK\n", __func__, wpriv);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530504 file->private_data = wpriv;
505
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800506 return 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530507}
508
509/*
510 * wdsp_glink_flush - Flush API to unblock read.
511 * file: Pointer to the file structure
512 * id: Lock owner ID
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800513 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530514 */
515static int wdsp_glink_flush(struct file *file, fl_owner_t id)
516{
517 struct wdsp_glink_priv *wpriv;
518
519 wpriv = (struct wdsp_glink_priv *)file->private_data;
520 if (!wpriv) {
521 pr_err("%s: Invalid private data\n", __func__);
522 return -EINVAL;
523 }
524
525 complete(&wpriv->rsp_complete);
526
527 return 0;
528}
529
530/*
531 * wdsp_glink_release - Release API to clean up resources.
532 * Whenever a file structure is shared across multiple threads,
533 * release won't be invoked until all copies are closed
534 * (file->f_count.counter should be 0). If we need to flush pending
535 * data when any copy is closed, you should implement the flush method.
536 *
537 * inode: Pointer to the inode structure
538 * file: Pointer to the file structure
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800539 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530540 */
541static int wdsp_glink_release(struct inode *inode, struct file *file)
542{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800543 pr_debug("%s: file->private_data = %pK\n", __func__,
544 file->private_data);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530545 file->private_data = NULL;
546
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800547 return 0;
548}
549
550static struct rpmsg_driver wdsp_rpmsg_driver = {
551 .probe = wdsp_rpmsg_probe,
552 .remove = wdsp_rpmsg_remove,
553 .callback = wdsp_rpmsg_callback,
554 /* Update this dynamically before register_rpmsg() */
555 .id_table = NULL,
556 .drv = {
557 .name = "wdsp_rpmsg",
558 },
559};
560
561static int wdsp_register_rpmsg(struct platform_device *pdev,
562 struct wdsp_glink_dev *wdev)
563{
564 int ret = 0;
565 int i, no_of_channels;
566 struct rpmsg_device_id *wdsp_rpmsg_id_table, *id_table;
567 const char *ch_name = NULL;
568
569 wpriv = devm_kzalloc(&pdev->dev,
570 sizeof(struct wdsp_glink_priv), GFP_KERNEL);
571 if (!wpriv)
572 return -ENOMEM;
573
574 no_of_channels = of_property_count_strings(pdev->dev.of_node,
Vidyakumar Athotae75ea782018-03-14 15:00:12 -0700575 "qcom,wdsp-channels");
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800576 if (no_of_channels < 0) {
577 dev_err(&pdev->dev, "%s: channel name parse error %d\n",
578 __func__, no_of_channels);
579 return -EINVAL;
580 }
581
582 wpriv->ch = devm_kzalloc(&pdev->dev,
583 (sizeof(struct wdsp_glink_priv *) * no_of_channels),
584 GFP_KERNEL);
585 if (!wpriv->ch)
586 return -ENOMEM;
587
588 for (i = 0; i < no_of_channels; i++) {
589 ret = of_property_read_string_index(pdev->dev.of_node,
590 "qcom,wdsp-channels", i,
591 &ch_name);
592 if (ret) {
593 dev_err(&pdev->dev, "%s: channel name parse error %d\n",
594 __func__, ret);
595 return -EINVAL;
596 }
597 wpriv->ch[i] = devm_kzalloc(&pdev->dev,
598 sizeof(struct wdsp_glink_priv),
599 GFP_KERNEL);
600 if (!wpriv->ch[i])
601 return -ENOMEM;
602
603 strlcpy(wpriv->ch[i]->ch_name, ch_name, RPMSG_NAME_SIZE);
604 wpriv->ch[i]->wpriv = wpriv;
605 spin_lock_init(&wpriv->ch[i]->ch_lock);
606 }
607 init_waitqueue_head(&wpriv->ch_state_wait);
608 init_completion(&wpriv->rsp_complete);
609 spin_lock_init(&wpriv->rsp_lock);
610
611 wpriv->wdev = wdev;
612 wpriv->dev = wdev->dev;
613 wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq");
614 if (!wpriv->work_queue) {
615 dev_err(&pdev->dev, "%s: Error creating wdsp_glink_wq\n",
616 __func__);
617 return -EINVAL;
618 }
619
620 wdsp_rpmsg_id_table = devm_kzalloc(&pdev->dev,
621 (sizeof(struct rpmsg_device_id) *
622 (no_of_channels + 1)),
623 GFP_KERNEL);
624 if (!wdsp_rpmsg_id_table) {
625 ret = -ENOMEM;
626 goto err;
627 }
628
629 wpriv->no_of_channels = no_of_channels;
630 id_table = wdsp_rpmsg_id_table;
631 for (i = 0; i < no_of_channels; i++) {
632 strlcpy(id_table->name, wpriv->ch[i]->ch_name,
633 RPMSG_NAME_SIZE);
634 id_table++;
635 }
636 wdsp_rpmsg_driver.id_table = wdsp_rpmsg_id_table;
637 ret = register_rpmsg_driver(&wdsp_rpmsg_driver);
638 if (ret < 0) {
639 dev_err(&pdev->dev, "%s: Rpmsg driver register failed, err = %d\n",
640 __func__, ret);
641 goto err;
642 }
643
644 return 0;
645
646err:
647 destroy_workqueue(wpriv->work_queue);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530648 return ret;
649}
650
651static const struct file_operations wdsp_glink_fops = {
652 .owner = THIS_MODULE,
653 .open = wdsp_glink_open,
654 .read = wdsp_glink_read,
655 .write = wdsp_glink_write,
656 .flush = wdsp_glink_flush,
657 .release = wdsp_glink_release,
658};
659
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530660static int wdsp_glink_probe(struct platform_device *pdev)
661{
662 int ret;
663 struct wdsp_glink_dev *wdev;
664
665 wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
666 if (!wdev) {
667 ret = -ENOMEM;
668 goto done;
669 }
670
671 ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT,
672 WDSP_GLINK_DRIVER_NAME);
673 if (ret < 0) {
674 dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n",
675 __func__, ret);
676 goto err_chrdev;
677 }
678
679 wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME);
680 if (IS_ERR(wdev->cls)) {
681 ret = PTR_ERR(wdev->cls);
682 dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n",
683 __func__, ret);
684 goto err_class;
685 }
686
687 wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num,
688 NULL, WDSP_GLINK_DRIVER_NAME);
689 if (IS_ERR(wdev->dev)) {
690 ret = PTR_ERR(wdev->dev);
691 dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n",
692 __func__, ret);
693 goto err_dev_create;
694 }
695
696 cdev_init(&wdev->cdev, &wdsp_glink_fops);
697 ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT);
698 if (ret < 0) {
699 dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n",
700 __func__, ret);
701 goto err_cdev_add;
702 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800703
704 ret = wdsp_register_rpmsg(pdev, wdev);
705 if (ret < 0) {
706 dev_err(&pdev->dev, "%s: Failed to register with rpmsg, err = %d\n",
707 __func__, ret);
708 goto err_cdev_add;
709 }
710 platform_set_drvdata(pdev, wpriv);
711
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530712 goto done;
713
714err_cdev_add:
715 device_destroy(wdev->cls, wdev->dev_num);
716
717err_dev_create:
718 class_destroy(wdev->cls);
719
720err_class:
721 unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
722
723err_chrdev:
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530724done:
725 return ret;
726}
727
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530728static int wdsp_glink_remove(struct platform_device *pdev)
729{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800730 struct wdsp_glink_priv *wpriv = platform_get_drvdata(pdev);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530731
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800732 unregister_rpmsg_driver(&wdsp_rpmsg_driver);
733
734 if (wpriv) {
735 flush_workqueue(wpriv->work_queue);
736 destroy_workqueue(wpriv->work_queue);
737 if (wpriv->wdev) {
738 cdev_del(&wpriv->wdev->cdev);
739 device_destroy(wpriv->wdev->cls, wpriv->wdev->dev_num);
740 class_destroy(wpriv->wdev->cls);
741 unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
742 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530743 }
744
745 return 0;
746}
747
748static const struct of_device_id wdsp_glink_of_match[] = {
749 {.compatible = "qcom,wcd-dsp-glink"},
750 { }
751};
752MODULE_DEVICE_TABLE(of, wdsp_glink_of_match);
753
754static struct platform_driver wdsp_glink_driver = {
755 .probe = wdsp_glink_probe,
756 .remove = wdsp_glink_remove,
757 .driver = {
758 .name = WDSP_GLINK_DRIVER_NAME,
759 .owner = THIS_MODULE,
760 .of_match_table = wdsp_glink_of_match,
761 },
762};
763
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530764static int __init wdsp_glink_init(void)
765{
766 return platform_driver_register(&wdsp_glink_driver);
767}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530768
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530769static void __exit wdsp_glink_exit(void)
770{
771 platform_driver_unregister(&wdsp_glink_driver);
772}
773
774module_init(wdsp_glink_init);
775module_exit(wdsp_glink_exit);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530776MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver");
777MODULE_LICENSE("GPL v2");