]> git.karo-electronics.de Git - karo-tx-linux.git/blob - sound/soc/qcom/qdsp6/core/apr_tal.c
Merge remote-tracking branch 'todor/release/qcomlt-4.4-camss-demo2' into release...
[karo-tx-linux.git] / sound / soc / qcom / qdsp6 / core / apr_tal.c
1 /* Copyright (c) 2015, The Linux Foundation. All rights reserved.
2  *
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/kernel.h>
14 #include <linux/module.h>
15 #include <linux/types.h>
16 #include <linux/uaccess.h>
17 #include <linux/spinlock.h>
18 #include <linux/mutex.h>
19 #include <linux/list.h>
20 #include <linux/sched.h>
21 #include <linux/wait.h>
22 #include <linux/errno.h>
23 #include <linux/platform_device.h>
24 #include <linux/delay.h>
25 #include <sound/qdsp6v2/apr_tal.h>
26 #include <linux/soc/qcom/smd.h>
27 #include <linux/io.h>
28
29 struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
30
31 int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len)
32 {
33         int ret;
34         ret = qcom_smd_send(apr_ch->ch, data, len);
35         if (ret) { 
36                 pr_err("apr_tal: Error in write %d\n", ret);
37                 return ret;;
38         }
39         return len;
40 }
41
42 struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest,
43                                 uint32_t dl, apr_svc_cb_fn func, void *priv)
44 {
45         int rc;
46         pr_err("apr_tal:open\n");
47         if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) ||
48                                                 (dl >= APR_DL_MAX)) {
49                 pr_err("apr_tal: Invalid params\n");
50                 return NULL;
51         }
52
53         if (apr_svc_ch[dl][dest][svc].ch) {
54                 pr_err("apr_tal: This channel alreday openend\n");
55                 return NULL;
56         }
57
58         if (!apr_svc_ch[dl][dest][svc].dest_state) {
59                 rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest,
60                         apr_svc_ch[dl][dest][svc].dest_state,
61                                 msecs_to_jiffies(APR_OPEN_TIMEOUT_MS));
62                 if (rc == 0) {
63                         pr_err("apr_tal:open timeout\n");
64                         return NULL;
65                 }
66                 pr_info("apr_tal:Wakeup done\n");
67         }
68         apr_svc_ch[dl][dest][svc].func = func;
69         apr_svc_ch[dl][dest][svc].priv = priv;
70
71         pr_info("apr_tal:apr svc init done\n");
72
73         return &apr_svc_ch[dl][dest][svc];
74 }
75
76 int apr_tal_close(struct apr_svc_ch_dev *apr_ch)
77 {
78         if (!apr_ch->ch)
79                 return -EINVAL;
80
81         apr_ch->ch = NULL;
82         apr_ch->func = NULL;
83         apr_ch->priv = NULL;
84         return 0;
85 }
86
87
88 static int qcom_smd_q6_callback(struct qcom_smd_channel *channel,
89                                  const void *data,
90                                  size_t count)
91 {
92         struct apr_svc_ch_dev *apr_ch = qcom_smd_get_drvdata(channel);
93
94         memcpy(apr_ch->data, data, count);
95
96         if (apr_ch->func)
97                 apr_ch->func(apr_ch->data, count, apr_ch->priv);
98
99         return 0;
100 }
101
102 static int qcom_smd_q6_probe(struct qcom_smd_device *sdev)
103 {
104         struct apr_svc_ch_dev *apr = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO];
105
106         pr_info("apr_tal:Q6 Is Up\n");
107
108         qcom_smd_set_drvdata(sdev->channel, apr);
109
110         apr->ch = sdev->channel;
111         apr->dest_state = 1;
112         wake_up(&apr->dest);
113
114         return 0;
115 }
116
117 static void qcom_smd_q6_remove(struct qcom_smd_device *sdev)
118 {
119         struct apr_svc_ch_dev *apr = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO];
120
121         apr->ch = NULL;
122         apr->dest_state = 0;
123 }
124
125
126 static const struct of_device_id qcom_smd_q6_of_match[] = {
127         { .compatible = "qcom,apr" },
128         {}
129 };
130
131 static struct qcom_smd_driver qcom_smd_q6_driver = {
132         .probe = qcom_smd_q6_probe,
133         .remove = qcom_smd_q6_remove,
134         .callback = qcom_smd_q6_callback,
135         .driver  = {
136                 .name  = "qcom_smd_q6",
137                 .owner = THIS_MODULE,
138                 .of_match_table = qcom_smd_q6_of_match,
139         },
140 };
141
142 static void __exit qcom_smd_q6_exit(void)
143 {
144         qcom_smd_driver_unregister(&qcom_smd_q6_driver);
145 }
146 module_exit(qcom_smd_q6_exit);
147
148 static int __init apr_tal_init(void)
149 {
150
151         int i, j, k;
152
153         for (i = 0; i < APR_DL_MAX; i++)
154                 for (j = 0; j < APR_DEST_MAX; j++)
155                         for (k = 0; k < APR_CLIENT_MAX; k++) {
156                                 init_waitqueue_head(&apr_svc_ch[i][j][k].wait);
157                                 init_waitqueue_head(&apr_svc_ch[i][j][k].dest);
158                         }
159         qcom_smd_driver_register(&qcom_smd_q6_driver);
160         return 0;
161 }
162 device_initcall(apr_tal_init);
163
164 MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
165 MODULE_DESCRIPTION("Qualcomm SMD backed apr driver");
166 MODULE_LICENSE("GPL v2");