Merge tag 'v4.4-rc5'
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rockchip-hdmi-cec.c
1 #include <linux/kernel.h>
2 #include <linux/slab.h>
3 #include <linux/workqueue.h>
4 #include <linux/delay.h>
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/errno.h>
8 #include <linux/string.h>
9 #include <linux/workqueue.h>
10 #include <linux/firmware.h>
11 #include "rockchip-hdmi-cec.h"
12 #include "linux/ioctl.h"
13 #include "linux/pagemap.h"
14
15 static struct cec_device *cec_dev;
16
17 static int cecreadframe(struct cec_framedata *frame)
18 {
19         if (frame == NULL || !cec_dev ||
20             cec_dev->readframe == NULL || !cec_dev->enable)
21                 return -1;
22         else
23                 return cec_dev->readframe(cec_dev->hdmi, frame);
24 }
25
26 static int cecsendframe(struct cec_framedata *frame)
27 {
28         if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
29                 return -1;
30         else
31                 return cec_dev->sendframe(cec_dev->hdmi, frame);
32 }
33
34 static void cecworkfunc(struct work_struct *work)
35 {
36         struct cec_delayed_work *cec_w =
37                 container_of(work, struct cec_delayed_work, work.work);
38         struct cecframelist *list_node;
39
40         switch (cec_w->event) {
41         case EVENT_ENUMERATE:
42                 break;
43         case EVENT_RX_FRAME:
44                 list_node = kmalloc(sizeof(*list_node), GFP_KERNEL);
45                 if (list_node == NULL) {
46                         pr_err("HDMI CEC: list kmalloc fail! ");
47                         return;
48                 }
49                 cecreadframe(&list_node->cecframe);
50                 if (cec_dev->enable) {
51                         mutex_lock(&cec_dev->cec_lock);
52                         list_add_tail(&(list_node->framelist),
53                                       &cec_dev->ceclist);
54                         sysfs_notify(&cec_dev->device.this_device->kobj,
55                                      NULL, "stat");
56                         mutex_unlock(&cec_dev->cec_lock);
57                 } else {
58                         kfree(list_node);
59                 }
60                 break;
61         default:
62                 break;
63         }
64
65         kfree(cec_w->data);
66         kfree(cec_w);
67 }
68
69 void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
70 {
71         struct cec_delayed_work *work;
72
73         CECDBG("%s event %04x delay %d\n", __func__, event, delay);
74
75         work = kmalloc(sizeof(*work), GFP_ATOMIC);
76
77         if (work) {
78                 INIT_DELAYED_WORK(&work->work, cecworkfunc);
79                 work->event = event;
80                 work->data = data;
81                 queue_delayed_work(cec_dev->workqueue,
82                                    &work->work,
83                                    msecs_to_jiffies(delay));
84         } else {
85                 CECDBG(KERN_WARNING "CEC: Cannot allocate memory\n");
86         }
87 }
88
89 void rockchip_hdmi_cec_set_pa(int devpa)
90 {
91         struct list_head *pos, *n;
92
93         if (cec_dev) {
94                 cec_dev->address_phy = devpa;
95                 pr_info("%s %x\n", __func__, devpa);
96                 /*when hdmi hpd , ceclist will be reset*/
97                 mutex_lock(&cec_dev->cec_lock);
98                 if (!list_empty(&cec_dev->ceclist)) {
99                         list_for_each_safe(pos, n, &cec_dev->ceclist) {
100                                 list_del(pos);
101                                 kfree(pos);
102                         }
103                 }
104                 INIT_LIST_HEAD(&cec_dev->ceclist);
105                 sysfs_notify(&cec_dev->device.this_device->kobj, NULL, "stat");
106                 mutex_unlock(&cec_dev->cec_lock);
107         }
108 }
109
110 static ssize_t  cec_enable_show(struct device *dev,
111                                 struct device_attribute *attr, char *buf)
112 {
113         return snprintf(buf, PAGE_SIZE, "%d\n", cec_dev->enable);
114 }
115
116 static ssize_t cec_enable_store(struct device *dev,
117                                 struct device_attribute *attr,
118                                 const char *buf, size_t count)
119 {
120         int ret;
121
122         ret = kstrtoint(buf, 0, &(cec_dev->enable));
123         return count;
124 }
125
126 static ssize_t  cec_phy_show(struct device *dev,
127                              struct device_attribute *attr, char *buf)
128 {
129         return snprintf(buf, PAGE_SIZE, "0x%x\n", cec_dev->address_phy);
130 }
131
132 static ssize_t cec_phy_store(struct device *dev,
133                              struct device_attribute *attr,
134                          const char *buf, size_t count)
135 {
136         int ret;
137
138         ret = kstrtoint(buf, 0, &(cec_dev->address_phy));
139         return count;
140 }
141
142 static ssize_t  cec_logic_show(struct device *dev,
143                                struct device_attribute *attr, char *buf)
144 {
145         return snprintf(buf, PAGE_SIZE, "0x%02x\n", cec_dev->address_logic);
146 }
147
148 static ssize_t cec_logic_store(struct device *dev,
149                                struct device_attribute *attr,
150                                const char *buf, size_t count)
151 {
152         int ret;
153
154         ret = kstrtoint(buf, 0, &(cec_dev->address_logic));
155         return count;
156 }
157
158 static ssize_t  cec_state_show(struct device *dev,
159                                struct device_attribute *attr, char *buf)
160 {
161         int stat;
162
163         mutex_lock(&cec_dev->cec_lock);
164         if (!cec_dev->address_phy)
165                 stat = 0;
166         else if (list_empty(&cec_dev->ceclist))
167                 stat = 1;
168         else
169                 stat = 2;
170         mutex_unlock(&cec_dev->cec_lock);
171         return snprintf(buf, PAGE_SIZE, "%d\n", stat);
172 }
173
174 static struct device_attribute cec_attrs[] = {
175         __ATTR(logic, 0666, cec_logic_show, cec_logic_store),
176         __ATTR(phy, 0666, cec_phy_show, cec_phy_store),
177         __ATTR(enable, 0666, cec_enable_show, cec_enable_store),
178         __ATTR(stat, S_IRUGO, cec_state_show, NULL),
179 };
180
181 static long cec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
182 {
183         int ret;
184         void __user *argp;
185         struct cec_framedata cecsendtemp;
186         struct cecframelist *listemp;
187
188         argp = (void __user *)arg;
189         switch (cmd) {
190         case HDMI_IOCTL_CECSETLA:
191                 ret = copy_from_user(&cec_dev->address_logic,
192                                      argp, sizeof(int));
193                 if (cec_dev->setceclogicaddr)
194                         cec_dev->setceclogicaddr(cec_dev->hdmi,
195                                                  cec_dev->address_logic);
196                 break;
197         case HDMI_IOCTL_CECSEND:
198                 ret = copy_from_user(&cecsendtemp, argp,
199                                      sizeof(struct cec_framedata));
200                 ret = cecsendframe(&cecsendtemp);
201                 cecsendtemp.returnval = ret;
202                 ret = copy_to_user(argp, &cecsendtemp,
203                                    sizeof(struct cec_framedata));
204                 break;
205         case HDMI_IOCTL_CECENAB:
206                 ret = copy_from_user(&cec_dev->enable, argp, sizeof(int));
207                 break;
208         case HDMI_IOCTL_CECPHY:
209                 ret = copy_to_user(argp, &(cec_dev->address_phy), sizeof(int));
210                 break;
211         case HDMI_IOCTL_CECLOGIC:
212                 ret = copy_to_user(argp, &(cec_dev->address_logic),
213                                    sizeof(int));
214                 break;
215         case HDMI_IOCTL_CECREAD:
216                 mutex_lock(&cec_dev->cec_lock);
217                 if (!list_empty(&cec_dev->ceclist)) {
218                         listemp = list_entry(cec_dev->ceclist.next,
219                                              struct cecframelist, framelist);
220                         ret = copy_to_user(argp, &listemp->cecframe,
221                                            sizeof(struct cec_framedata));
222                         list_del(&listemp->framelist);
223                         kfree(listemp);
224                 }
225                 mutex_unlock(&cec_dev->cec_lock);
226                 break;
227         case HDMI_IOCTL_CECCLEARLA:
228                 break;
229         case HDMI_IOCTL_CECWAKESTATE:
230                 ret = copy_to_user(argp, &(cec_dev->hdmi->sleep), sizeof(int));
231                 break;
232
233         default:
234                 break;
235         }
236         return 0;
237 }
238
239 static const struct file_operations cec_fops = {
240         .owner          = THIS_MODULE,
241         .compat_ioctl   = cec_ioctl,
242         .unlocked_ioctl = cec_ioctl,
243 };
244
245 int rockchip_hdmi_cec_init(struct hdmi *hdmi,
246                            int (*sendframe)(struct hdmi *,
247                                             struct cec_framedata *),
248                            int (*readframe)(struct hdmi *,
249                                             struct cec_framedata *),
250                            void (*setceclogicaddr)(struct hdmi *, int))
251 {
252         int ret, i;
253
254         cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
255         if (!cec_dev) {
256                 pr_err("HDMI CEC: kmalloc fail!");
257                 return -ENOMEM;
258         }
259         memset(cec_dev, 0, sizeof(struct cec_device));
260         mutex_init(&cec_dev->cec_lock);
261         INIT_LIST_HEAD(&cec_dev->ceclist);
262         cec_dev->hdmi = hdmi;
263         cec_dev->enable = 1;
264         cec_dev->sendframe = sendframe;
265         cec_dev->readframe = readframe;
266         cec_dev->setceclogicaddr = setceclogicaddr;
267         cec_dev->workqueue = create_singlethread_workqueue("hdmi-cec");
268         if (cec_dev->workqueue == NULL) {
269                 pr_err("HDMI CEC: create workqueue failed.\n");
270                 return -1;
271         }
272         cec_dev->device.minor = MISC_DYNAMIC_MINOR;
273         cec_dev->device.name = "cec";
274         cec_dev->device.mode = 0666;
275         cec_dev->device.fops = &cec_fops;
276         if (misc_register(&cec_dev->device)) {
277                 pr_err("CEC: Could not add cec misc driver\n");
278                 goto error;
279         }
280         for (i = 0; i < ARRAY_SIZE(cec_attrs); i++) {
281                 ret = device_create_file(cec_dev->device.this_device,
282                                          &cec_attrs[i]);
283                 if (ret) {
284                         pr_err("CEC: Could not add sys file\n");
285                         goto error1;
286                 }
287         }
288         return 0;
289
290 error1:
291         misc_deregister(&cec_dev->device);
292 error:
293         return -EINVAL;
294 }