blob: a6eb044ff262c4a0de7518701d94a93da33dd48b [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Meng Wang61af6842018-09-10 17:47:55 +08002/*
Xiaojun Sang53cd13a2018-06-29 15:14:37 +08003 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
Laxminath Kasamd1f36192017-08-03 18:54:01 +05304 */
5
6#include <linux/init.h>
7#include <linux/kernel.h>
8#include <linux/module.h>
Vidyakumar Athota7206c722017-12-05 11:43:47 -08009#include <linux/spinlock.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053010#include <linux/errno.h>
11#include <linux/fs.h>
12#include <linux/uaccess.h>
13#include <linux/slab.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053014#include <linux/cdev.h>
15#include <linux/platform_device.h>
Vidyakumar Athota7206c722017-12-05 11:43:47 -080016#include <linux/of_device.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053017#include <linux/vmalloc.h>
Vidyakumar Athota7206c722017-12-05 11:43:47 -080018#include <linux/rpmsg.h>
Laxminath Kasamd1f36192017-08-03 18:54:01 +053019#include "sound/wcd-dsp-glink.h"
20
21#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink"
22#define WDSP_MAX_WRITE_SIZE (256 * 1024)
23#define WDSP_MAX_READ_SIZE (4 * 1024)
Laxminath Kasamd1f36192017-08-03 18:54:01 +053024#define WDSP_WRITE_PKT_SIZE (sizeof(struct wdsp_write_pkt))
Laxminath Kasamd1f36192017-08-03 18:54:01 +053025#define WDSP_CMD_PKT_SIZE (sizeof(struct wdsp_cmd_pkt))
Laxminath Kasamd1f36192017-08-03 18:54:01 +053026
27#define MINOR_NUMBER_COUNT 1
Laxminath Kasamd1f36192017-08-03 18:54:01 +053028#define RESP_QUEUE_SIZE 3
Vidyakumar Athota71699fd2018-10-16 11:32:47 -070029#define TIMEOUT_MS 2000
Laxminath Kasamd1f36192017-08-03 18:54:01 +053030
Vidyakumar Athota7206c722017-12-05 11:43:47 -080031enum wdsp_ch_state {
32 WDSP_CH_DISCONNECTED,
33 WDSP_CH_CONNECTED,
34};
35
Laxminath Kasamd1f36192017-08-03 18:54:01 +053036struct wdsp_glink_dev {
37 struct class *cls;
38 struct device *dev;
39 struct cdev cdev;
40 dev_t dev_num;
41};
42
Vidyakumar Athota7206c722017-12-05 11:43:47 -080043struct wdsp_rsp_que {
Laxminath Kasamd1f36192017-08-03 18:54:01 +053044 /* Size of valid data in buffer */
45 u32 buf_size;
46
47 /* Response buffer */
48 u8 buf[WDSP_MAX_READ_SIZE];
49};
50
Vidyakumar Athota7206c722017-12-05 11:43:47 -080051struct wdsp_ch {
52 struct wdsp_glink_priv *wpriv;
53 /* rpmsg handle */
54 void *handle;
55 /* Channel states like connect, disconnect */
56 int ch_state;
57 char ch_name[RPMSG_NAME_SIZE];
58 spinlock_t ch_lock;
59};
60
61struct wdsp_tx_buf {
Laxminath Kasamd1f36192017-08-03 18:54:01 +053062 struct work_struct tx_work;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053063
64 /* Glink channel information */
Vidyakumar Athota7206c722017-12-05 11:43:47 -080065 struct wdsp_ch *ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053066
67 /* Tx buffer to send to glink */
68 u8 buf[0];
69};
70
Laxminath Kasamd1f36192017-08-03 18:54:01 +053071struct wdsp_glink_priv {
72 /* Respone buffer related */
73 u8 rsp_cnt;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080074 struct wdsp_rsp_que rsp[RESP_QUEUE_SIZE];
Xiaojun Sang5e8453b2018-06-15 17:03:42 +080075 u8 write_idx;
76 u8 read_idx;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053077 struct completion rsp_complete;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080078 spinlock_t rsp_lock;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053079
80 /* Glink channel related */
Vidyakumar Athota7206c722017-12-05 11:43:47 -080081 int no_of_channels;
82 struct wdsp_ch **ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053083 struct workqueue_struct *work_queue;
Vidyakumar Athota7206c722017-12-05 11:43:47 -080084 /* Wait for all channels state before sending any command */
85 wait_queue_head_t ch_state_wait;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053086
Vidyakumar Athota7206c722017-12-05 11:43:47 -080087 struct wdsp_glink_dev *wdev;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053088 struct device *dev;
89};
Vidyakumar Athota7206c722017-12-05 11:43:47 -080090static struct wdsp_glink_priv *wpriv;
Laxminath Kasamd1f36192017-08-03 18:54:01 +053091
Vidyakumar Athota7206c722017-12-05 11:43:47 -080092static struct wdsp_ch *wdsp_get_ch(char *ch_name)
Laxminath Kasamd1f36192017-08-03 18:54:01 +053093{
94 int i;
95
Laxminath Kasamd1f36192017-08-03 18:54:01 +053096 for (i = 0; i < wpriv->no_of_channels; i++) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -080097 if (!strcmp(ch_name, wpriv->ch[i]->ch_name))
98 return wpriv->ch[i];
Laxminath Kasamd1f36192017-08-03 18:54:01 +053099 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800100 return NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530101}
102
103/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800104 * wdsp_rpmsg_callback - Rpmsg callback for responses
105 * rpdev: Rpmsg device structure
106 * data: Pointer to the Rx data
107 * len: Size of the Rx data
108 * priv: Private pointer to the channel
109 * addr: Address variable
110 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530111 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800112static int wdsp_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
113 int len, void *priv, u32 addr__unused)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530114{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800115 struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530116 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800117 unsigned long flags;
118 u8 *rx_buf;
119 u8 rsp_cnt = 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530120
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800121 if (!ch || !data) {
122 pr_err("%s: Invalid ch or data\n", __func__);
123 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530124 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800125
126 wpriv = ch->wpriv;
127 rx_buf = (u8 *)data;
128 if (len > WDSP_MAX_READ_SIZE) {
129 dev_info_ratelimited(wpriv->dev, "%s: Size %d is greater than allowed %d\n",
130 __func__, len, WDSP_MAX_READ_SIZE);
131 len = WDSP_MAX_READ_SIZE;
132 }
133 dev_dbg_ratelimited(wpriv->dev, "%s: copy into buffer %d\n", __func__,
134 wpriv->rsp_cnt);
135
136 if (wpriv->rsp_cnt >= RESP_QUEUE_SIZE) {
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800137 dev_info_ratelimited(wpriv->dev, "%s: Resp Queue is Full. Ignore new one.\n",
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800138 __func__);
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800139 return -EINVAL;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800140 }
141 spin_lock_irqsave(&wpriv->rsp_lock, flags);
142 rsp_cnt = wpriv->rsp_cnt;
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800143 memcpy(wpriv->rsp[wpriv->write_idx].buf, rx_buf, len);
144 wpriv->rsp[wpriv->write_idx].buf_size = len;
145 wpriv->write_idx = (wpriv->write_idx + 1) % RESP_QUEUE_SIZE;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800146 wpriv->rsp_cnt = ++rsp_cnt;
147 spin_unlock_irqrestore(&wpriv->rsp_lock, flags);
148
149 complete(&wpriv->rsp_complete);
150
151 return 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530152}
153
154/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800155 * wdsp_rpmsg_probe - Rpmsg channel probe function
156 * rpdev: Rpmsg device structure
157 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530158 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800159static int wdsp_rpmsg_probe(struct rpmsg_device *rpdev)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530160{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800161 struct wdsp_ch *ch;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530162
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800163 ch = wdsp_get_ch(rpdev->id.name);
164 if (!ch) {
165 dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n",
166 __func__, rpdev->id.name);
167 return -EINVAL;
168 }
169
170 dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Up]\n",
171 __func__, rpdev->id.name);
172
173 spin_lock(&ch->ch_lock);
174 ch->handle = rpdev;
175 ch->ch_state = WDSP_CH_CONNECTED;
176 spin_unlock(&ch->ch_lock);
177 dev_set_drvdata(&rpdev->dev, ch);
178 wake_up(&wpriv->ch_state_wait);
179
180 return 0;
181}
182
183/*
184 * wdsp_rpmsg_remove - Rpmsg channel remove function
185 * rpdev: Rpmsg device structure
186 */
187static void wdsp_rpmsg_remove(struct rpmsg_device *rpdev)
188{
189 struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev);
190
191 if (!ch) {
192 dev_err(&rpdev->dev, "%s: Invalid ch\n", __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530193 return;
194 }
195
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800196 dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Down]\n",
197 __func__, rpdev->id.name);
198 spin_lock(&ch->ch_lock);
199 ch->handle = NULL;
200 ch->ch_state = WDSP_CH_DISCONNECTED;
201 spin_unlock(&ch->ch_lock);
202 dev_set_drvdata(&rpdev->dev, NULL);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530203}
204
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800205static bool wdsp_is_ch_connected(struct wdsp_glink_priv *wpriv)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530206{
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800207 int i;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530208
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800209 for (i = 0; i < wpriv->no_of_channels; i++) {
210 spin_lock(&wpriv->ch[i]->ch_lock);
211 if (wpriv->ch[i]->ch_state != WDSP_CH_CONNECTED) {
212 spin_unlock(&wpriv->ch[i]->ch_lock);
213 return false;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530214 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800215 spin_unlock(&wpriv->ch[i]->ch_lock);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530216 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800217 return true;
218}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530219
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800220static int wdsp_wait_for_all_ch_connect(struct wdsp_glink_priv *wpriv)
221{
222 int ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530223
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800224 ret = wait_event_timeout(wpriv->ch_state_wait,
225 wdsp_is_ch_connected(wpriv),
226 msecs_to_jiffies(TIMEOUT_MS));
227 if (!ret) {
228 dev_err_ratelimited(wpriv->dev, "%s: All channels are not connected\n",
229 __func__);
230 ret = -ETIMEDOUT;
231 goto err;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530232 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800233 ret = 0;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530234
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800235err:
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530236 return ret;
237}
238
239/*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800240 * wdsp_tx_buf_work - Work queue function to send tx buffer to glink
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530241 * work: Work structure
242 */
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800243static void wdsp_tx_buf_work(struct work_struct *work)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530244{
245 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800246 struct wdsp_ch *ch;
247 struct wdsp_tx_buf *tx_buf;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530248 struct wdsp_write_pkt *wpkt;
249 struct wdsp_cmd_pkt *cpkt;
250 int ret = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800251 struct rpmsg_device *rpdev = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530252
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800253 tx_buf = container_of(work, struct wdsp_tx_buf,
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530254 tx_work);
255 ch = tx_buf->ch;
256 wpriv = ch->wpriv;
257 wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
258 cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800259
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530260 dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n",
261 __func__, cpkt->ch_name, cpkt->payload_size);
262
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800263 spin_lock(&ch->ch_lock);
264 rpdev = ch->handle;
Xiaoyu Ye7c54a182018-03-22 11:55:06 -0700265 if (rpdev && ch->ch_state == WDSP_CH_CONNECTED) {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800266 spin_unlock(&ch->ch_lock);
267 ret = rpmsg_send(rpdev->ept, cpkt->payload,
268 cpkt->payload_size);
269 if (ret < 0)
270 dev_err(wpriv->dev, "%s: rpmsg send failed, ret = %d\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530271 __func__, ret);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530272 } else {
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800273 spin_unlock(&ch->ch_lock);
Xiaoyu Ye7c54a182018-03-22 11:55:06 -0700274 if (rpdev)
275 dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
276 __func__, ch->ch_name);
277 else
278 dev_err(wpriv->dev, "%s: rpdev is NULL\n", __func__);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530279 }
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800280 vfree(tx_buf);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530281}
282
283/*
284 * wdsp_glink_read - Read API to send the data to userspace
285 * file: Pointer to the file structure
286 * buf: Pointer to the userspace buffer
287 * count: Number bytes to read from the file
288 * ppos: Pointer to the position into the file
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800289 * Returns 0 on success and an appropriate error value on failure
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530290 */
291static ssize_t wdsp_glink_read(struct file *file, char __user *buf,
292 size_t count, loff_t *ppos)
293{
294 int ret = 0, ret1 = 0;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800295 struct wdsp_rsp_que *read_rsp = NULL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530296 struct wdsp_glink_priv *wpriv;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800297 unsigned long flags;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530298
299 wpriv = (struct wdsp_glink_priv *)file->private_data;
300 if (!wpriv) {
301 pr_err("%s: Invalid private data\n", __func__);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800302 return -EINVAL;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530303 }
304
305 if (count > WDSP_MAX_READ_SIZE) {
Vidyakumar Athota60871bd2017-09-08 11:26:51 -0700306 dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n",
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530307 __func__, count);
308 count = WDSP_MAX_READ_SIZE;
309 }
310 /*
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800311 * Complete signal has given from gwdsp_rpmsg_callback()
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530312 * or from flush API. Also use interruptible wait_for_completion API
313 * to allow the system to go in suspend.
314 */
315 ret = wait_for_completion_interruptible(&wpriv->rsp_complete);
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800316 if (ret < 0)
317 return ret;
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530318
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800319 read_rsp = kzalloc(sizeof(struct wdsp_rsp_que), GFP_KERNEL);
320 if (!read_rsp)
321 return -ENOMEM;
322
323 spin_lock_irqsave(&wpriv->rsp_lock, flags);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530324 if (wpriv->rsp_cnt) {
325 wpriv->rsp_cnt--;
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800326 dev_dbg(wpriv->dev, "%s: rsp_cnt=%d read from buffer %d\n",
327 __func__, wpriv->rsp_cnt, wpriv->read_idx);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530328
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800329 memcpy(read_rsp, &wpriv->rsp[wpriv->read_idx],
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800330 sizeof(struct wdsp_rsp_que));
Xiaojun Sang5e8453b2018-06-15 17:03:42 +0800331 wpriv->read_idx = (wpriv->read_idx + 1) % RESP_QUEUE_SIZE;
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800332 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,
Vidyakumar Athotae75ea782018-03-14 15:00:12 -0700572 "qcom,wdsp-channels");
Vidyakumar Athota7206c722017-12-05 11:43:47 -0800573 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,
Xiaojun Sang53cd13a2018-06-29 15:14:37 +0800758 .suppress_bind_attrs = true,
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530759 },
760};
761
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530762static int __init wdsp_glink_init(void)
763{
764 return platform_driver_register(&wdsp_glink_driver);
765}
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530766
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530767static void __exit wdsp_glink_exit(void)
768{
769 platform_driver_unregister(&wdsp_glink_driver);
770}
771
772module_init(wdsp_glink_init);
773module_exit(wdsp_glink_exit);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530774MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver");
775MODULE_LICENSE("GPL v2");